Files
NianAIGC/backend/internal/httpapi/layer_compositions_test.go

95 lines
4.0 KiB
Go

package httpapi
import (
"context"
"net/http"
"strings"
"testing"
"time"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/assets"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/identity"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/jobs"
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/layercompositions"
)
type layerCompositionRepositoryStub struct {
value layercompositions.Composition
found bool
}
func (repository *layerCompositionRepositoryStub) FindLayerComposition(_ context.Context, ownerID, jobID string) (layercompositions.Composition, bool, error) {
if !repository.found || repository.value.OwnerID != ownerID || repository.value.JobID != jobID {
return layercompositions.Composition{}, false, nil
}
return repository.value, true, nil
}
func (repository *layerCompositionRepositoryStub) SaveLayerComposition(_ context.Context, value layercompositions.Composition, version int64) (layercompositions.Composition, error) {
if repository.found && repository.value.Version != version {
return layercompositions.Composition{}, layercompositions.ErrConflict
}
if !repository.found && version != 0 {
return layercompositions.Composition{}, layercompositions.ErrConflict
}
value.Version = version + 1
value.CreatedAt = time.Date(2026, 9, 2, 8, 0, 0, 0, time.UTC)
value.UpdatedAt = value.CreatedAt
repository.value = value
repository.found = true
return value, nil
}
type layerCompositionAssetCatalog map[string]assets.Asset
func (catalog layerCompositionAssetCatalog) GetOwner(_ context.Context, ownerID, assetID string) (assets.Asset, bool, error) {
asset, found := catalog[assetID]
return asset, found && asset.OwnerID == ownerID, nil
}
func TestLayerCompositionsHTTPPersistsOnlyOwnedJobOutputsAndAssets(t *testing.T) {
jobStore := &httpJobStore{values: map[string]jobs.Job{
"job-1": {ID: "job-1", OwnerID: "owner", Capability: "image.generate", OutputAssetIDs: []string{"base"}},
"job-2": {ID: "job-2", OwnerID: "another-owner", Capability: "image.generate", OutputAssetIDs: []string{"foreign-base"}},
}}
jobService := jobs.NewService(jobStore, nil)
repository := &layerCompositionRepositoryStub{}
compositionService := layercompositions.NewService(repository, layerCompositionAssetCatalog{
"base": {ID: "base", OwnerID: "owner", Kind: assets.KindImage},
"layer": {ID: "layer", OwnerID: "owner", Kind: assets.KindImage},
})
authorizer, err := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, &fixedSessionResolver{session: identity.Session{User: identity.User{ID: "owner", ClientID: "platform", Role: "user"}}})
if err != nil {
t.Fatal(err)
}
handler, err := NewLayerCompositionsHandler(authorizer, jobService, compositionService, LayerCompositionsConfig{})
if err != nil {
t.Fatal(err)
}
missing := serveJSON(t, handler, http.MethodGet, "/api/layer-compositions/job-1", nil)
if missing.Code != http.StatusOK || strings.TrimSpace(missing.Body.String()) != `{"composition":null}` {
t.Fatalf("missing=%d %s", missing.Code, missing.Body.String())
}
saved := serveJSON(t, handler, http.MethodPut, "/api/layer-compositions/job-1", map[string]any{
"version": 0, "baseAssetId": "base", "baseVisible": true,
"layers": []any{map[string]any{"id": "layer-1", "assetId": "layer", "name": "主体", "visible": true, "box": []any{100, 100, 900, 900}, "rotation": 15, "source": "provider"}},
})
if saved.Code != http.StatusOK || repository.value.Version != 1 || repository.value.Layers[0].Rotation != 15 {
t.Fatalf("saved=%d %s value=%#v", saved.Code, saved.Body.String(), repository.value)
}
foreignJob := serveJSON(t, handler, http.MethodGet, "/api/layer-compositions/job-2", nil)
if foreignJob.Code != http.StatusNotFound {
t.Fatalf("foreign job=%d %s", foreignJob.Code, foreignJob.Body.String())
}
invalidBase := serveJSON(t, handler, http.MethodPut, "/api/layer-compositions/job-1", map[string]any{
"version": 1, "baseAssetId": "layer", "baseVisible": true, "layers": []any{},
})
if invalidBase.Code != http.StatusBadRequest {
t.Fatalf("invalid base=%d %s", invalidBase.Code, invalidBase.Body.String())
}
}