test(mirror): cover onCreated callback sequencing in createAndAwaitMirror · Entire

test(mirror): cover onCreated callback sequencing in createAndAwaitMirror

6b4b4d6→main·

toothbrush·2w ago·2 files·+110 added/-5 removed

Assert onCreated fires exactly once on CreateMirror success, before any clone polling/onStatus, and never on a CreateMirror error — pinning the placing-vs-cloning phase split.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

bfb60c5675abView transcript

[?
Add Mirror onCreated Callback TestClaude Code·Opus 4.8[1m]·1 step](/content/gh/entireio/cli/session/cfaebac5-e82a-472d-abda-b5ee328024d4#timeline-bfb60c5675ab/index.html)

Changes

2

69 unmodified lines

func TestMirrorList_FollowsCursor(t *testing.T) {
    var gotTokens []string
    srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/api/v1/mirrors" {
            if r.URL.Path != mirrorsAPIPath {
                 t.Errorf("unexpected path %q", r.URL.Path)
            }
            token := r.URL.Query().Get("pageToken")

Mcmd/entire/cli/list_pagination_test.go+1/-1

15 unmodified lines

16
17
18
19
20
21
22
23
24
25
99 unmodified lines

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
98 unmodified lines

330
331
332
228
333
334
335
336
63 unmodified lines

400
401
402
298
403
404
405
406
17 unmodified lines

424
425
426
322
427
428
429
430
3 unmodified lines

434
435
436
332
437
438
439
440

15 unmodified lines

"github.com/entireio/cli/internal/coreapi"

// mirrorsAPIPath is the control-plane mirrors collection endpoint, shared by the
// fake servers in these tests.
const mirrorsAPIPath = "/api/v1/mirrors"

func TestExplainSuspendedMirror(t *testing.T) {
    t.Parallel()
    const id = "01KS6KFJR2XS6PZ188MVYE07AN"
99 unmodified lines

}

// serveMirrorCreate stands up a control plane that answers POST /mirrors with
// the given CreatedMirror (or a 500 when createErr) and GET /mirrors/{id} with
// a Ready status, then points createAndAwaitMirror's client at it. It records
// the ordered request paths so tests can assert create-before-poll sequencing.
func serveMirrorCreate(t *testing.T, created *coreapi.CreatedMirror, createErr bool) (*coreapi.Client, *[]string) {
    t.Helper()
    var paths []string
    srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        paths = append(paths, r.URL.Path)
        w.Header().Set("Content-Type", "application/json")
        switch {
        case r.Method == http.MethodPost && r.URL.Path == mirrorsAPIPath:
            if createErr {
                w.WriteHeader(http.StatusInternalServerError)
                return
            }
            w.WriteHeader(http.StatusCreated)
            if err := writeJSON(w, created); err != nil {
                t.Errorf("encode created response: %v", err)
            }
        case r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/api/v1/mirrors/"):
            m := &coreapi.Mirror{}
            m.Status = coreapi.NewOptMirrorStatus(coreapi.MirrorStatusReady)
            if err := writeJSON(w, m); err != nil {
                t.Errorf("encode mirror response: %v", err)
            }
        default:
            t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
            w.WriteHeader(http.StatusNotFound)
        }
    }))
    t.Cleanup(srv.Close)
    c, err := coreapi.NewWithBearer(srv.URL, "tok")
    require.NoError(t, err)
    return c, &paths
}

// TestCreateAndAwaitMirror_OnCreated pins the onCreated callback contract: it
// delimits the placing vs cloning phases, so it must fire exactly once on
// CreateMirror success (before any clone polling / onStatus), and never on a
// CreateMirror error.
//
// Not parallel: shortens the package-level mirrorPollInterval.
func TestCreateAndAwaitMirror_OnCreated(t *testing.T) {
    prev := mirrorPollInterval
    mirrorPollInterval = time.Millisecond
    t.Cleanup(func() { mirrorPollInterval = prev })
    ctx := t.Context()

mk := func() *coreapi.CreatedMirror {
        return &coreapi.CreatedMirror{Created: true, MirrorId: "m1", MirrorUrl: "entire://c/gh/o/r"}
    }

t.Run("fires once before onStatus on success", func(t *testing.T) {
        c, _ := serveMirrorCreate(t, mk(), false)
        var events []string
        outcome, err := createAndAwaitMirror(ctx, c, "o", "r", "c", false, time.Second,
            func(m *coreapi.CreatedMirror) {
                require.Equal(t, "m1", m.MirrorId, "onCreated receives the create response")
                events = append(events, "created")
            },
            func(coreapi.MirrorStatus) { events = append(events, "status") },
        )
        require.NoError(t, err)
        require.Equal(t, coreapi.MirrorStatusReady, outcome.status)
        require.NotEmpty(t, events)
        require.Equal(t, "created", events[0], "onCreated must fire before any onStatus")
        require.Equal(t, 1, countEq(events, "created"), "onCreated fires exactly once")
    })

t.Run("does not fire on CreateMirror error", func(t *testing.T) {
        c, _ := serveMirrorCreate(t, nil, true)
        fired := 0
        outcome, err := createAndAwaitMirror(ctx, c, "o", "r", "c", false, time.Second,
            func(*coreapi.CreatedMirror) { fired++ }, nil)
        require.Error(t, err)
        require.Nil(t, outcome.created)
        require.Zero(t, fired, "onCreated must not fire when create fails")
    })

t.Run("fires once even with no-wait (no polling)", func(t *testing.T) {
        c, paths := serveMirrorCreate(t, mk(), false)
        fired := 0
        _, err := createAndAwaitMirror(ctx, c, "o", "r", "c", true, time.Second,
            func(*coreapi.CreatedMirror) { fired++ }, nil)
        require.NoError(t, err)
        require.Equal(t, 1, fired)
        require.Equal(t, []string{mirrorsAPIPath}, *paths, "no-wait must not poll GetMirror")
    })
}

func countEq(xs []string, want string) int {
    n := 0
    for _, x := range xs {
        if x == want {
            n++
        }
    }
    return n
}

// TestReportOneShotMirror exercises the one-shot create's presentation across
// the shared lifecycle outcomes — the branching finishMirrorCreate used to own,
// now driven by mirrorCreateOutcome (and shared with the wizard).
98 unmodified lines

if err := writeJSON(w, &coreapi.ListAvailableMirrorsOutputBody{Available: available}); err != nil {
            t.Errorf("encode available response: %v", err)
        }
        case "/api/v1/mirrors":
        case mirrorsAPIPath:
            if err := writeJSON(w, &coreapi.ListMirrorsOutputBody{Mirrors: mirrors}); err != nil {
                t.Errorf("encode mirrors response: %v", err)
        }
63 unmodified lines

stdout, stderr := runMirrorList(t)
    rec := <-recCh

require.Equal(t, "/api/v1/mirrors", rec.path)
    require.Equal(t, mirrorsAPIPath, rec.path)
    require.Contains(t, stderr, "Listing mirrors on")
    require.Contains(t, stdout, "CLONE URL")
    require.Contains(t, stdout, "entire://aws-us-east-2.entire.io/gh/acme/web")
17 unmodified lines

runMirrorList(t, "--owner", "acme")
    rec := <-recCh

require.Equal(t, "/api/v1/mirrors", rec.path)
    require.Equal(t, mirrorsAPIPath, rec.path)
    require.Equal(t, "acme", rec.query.Get("owner"))

}

3 unmodified lines

)
    runMirrorList(t, "--cluster", "eu-west-1.entire.io", "--provider", "github")
    rec := <-recCh
    require.Equal(t, "/api/v1/mirrors", rec.path)
    require.Equal(t, mirrorsAPIPath, rec.path)
    require.Equal(t, "eu-west-1.entire.io", rec.query.Get("cluster"))
    require.Equal(t, "github", rec.query.Get("provider"))

Mcmd/entire/cli/repo_mirror_test.go+109/-4