package e2e import ( "context" "path/filepath" "testing" "time" "galo/internal/albums" "galo/internal/photos" ) // TestEditPendingLifecycle covers the render-state flag clients use to decide // whether the edit_* URLs can be shown: saving edits reports a pending render, // the worker clears it and publishes the fresh URLs, a second edit keeps the // previous render (still pending), and a reset clears both the flag and the // edited version. func TestEditPendingLifecycle(t *testing.T) { ctx := context.Background() st, closeDB := openStack(t, filepath.Join(t.TempDir(), "galo.db"), t.TempDir()) defer closeDB() user, _, err := st.auth.Register(ctx, "editpending@test.local", "", "password123", "E") if err != nil { t.Fatalf("register: %v", err) } album, err := st.albums.Create(ctx, user.ID, albums.CreateInput{Title: "Pending"}) if err != nil { t.Fatalf("create album: %v", err) } p, err := st.photos.Upload(ctx, photos.UploadInput{ AlbumID: album.ID, UserID: user.ID, Filename: "p.png", Data: makePNG(t), }) if err != nil { t.Fatalf("upload: %v", err) } w := photos.NewWorker(st.photos, time.Second, 10, time.Minute, 2) w.ProcessOnce(ctx) first, err := st.photos.EditPhoto(ctx, p.ID, user.ID, photos.EditParams{ Light: photos.LightParams{Exposure: 0.5}, }) if err != nil { t.Fatalf("edit: %v", err) } if !first.EditPending { t.Fatalf("edit response does not report a pending render: %+v", first) } if first.EditPreviewURL != "" { t.Fatalf("first render already exposes edit_preview_url: %q", first.EditPreviewURL) } w.ProcessOnce(ctx) rendered, err := st.photos.Get(ctx, p.ID, user.ID) if err != nil { t.Fatalf("get rendered photo: %v", err) } if rendered.EditPending { t.Fatalf("rendered photo still reports edit_pending: %+v", rendered) } if rendered.EditPreviewURL == "" { t.Fatalf("rendered photo has no edit_preview_url: %+v", rendered) } firstPreview := rendered.EditPreviewURL // A second edit is queued while the previous render is still bound: the // photo stays pending and the old URLs must not be treated as current. second, err := st.photos.EditPhoto(ctx, p.ID, user.ID, photos.EditParams{ Light: photos.LightParams{Exposure: 1.25}, }) if err != nil { t.Fatalf("second edit: %v", err) } if !second.EditPending { t.Fatalf("second edit does not report a pending render: %+v", second) } if second.EditPreviewURL != firstPreview { t.Fatalf("second edit should keep the previous render while pending: %q want %q", second.EditPreviewURL, firstPreview) } w.ProcessOnce(ctx) after, err := st.photos.Get(ctx, p.ID, user.ID) if err != nil { t.Fatalf("get after second render: %v", err) } if after.EditPending { t.Fatalf("second render still reports edit_pending: %+v", after) } if after.EditPreviewURL == "" || after.EditPreviewURL == firstPreview { t.Fatalf("second render preview url = %q, want a fresh one (old %q)", after.EditPreviewURL, firstPreview) } reset, err := st.photos.ResetEdit(ctx, p.ID, user.ID) if err != nil { t.Fatalf("reset: %v", err) } if reset.EditPending { t.Fatalf("reset photo reports edit_pending: %+v", reset) } if reset.EditPreviewURL != "" || reset.EditURL != "" { t.Fatalf("reset photo still exposes edit urls: %+v", reset) } if reset.EditParams != nil && len(reset.EditParams) > 0 { t.Fatalf("reset photo still exposes edit params: %+v", reset.EditParams) } }