Files
pad/internal/server/bug3658_relation_defaults_header_test.go
T
xarmian 77e1b386ef fix(import): relation defaults in imported schemas point at the imported items (BUG-3658) (#2159)
* fix(import): relation defaults in imported schemas point at the imported items (BUG-3658)

The collection insert stored the bundle's schema verbatim, so a relation or
multi_relation default named an item by its SOURCE id. In the new workspace
that id names nothing, and the next create dropped the default (or refused,
for a required field). Once every item has its new id, each imported schema's
relation defaults are remapped through the inserted-item map. An id the
import has no item for is dropped, logged and reported (ImportReport.
DroppedRelationDefaults, header X-Pad-Import-Dropped-Relation-Defaults); a
value that is not an id, such as a ref, is kept, since it resolves late. The
schema is re-encoded only when it changes, through a generic decode so keys
this build does not model survive.

Red on main: the single and multi defaults kept the source ids, the dangling
one was not dropped, nothing was reported; the header test is red without the
header line.

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS

* docs(api): the import doors report dropped relation defaults in a header (BUG-3658)

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS
2026-10-14 08:53:26 -04:00

48 lines
1.7 KiB
Go

package server
import (
"encoding/json"
"net/http"
"testing"
"github.com/PerpetualSoftware/pad/internal/models"
)
// BUG-3658, the import door's surface: the response header counts the
// relation defaults the import dropped because they named no imported item,
// and is "0" when none were. The store test pins what is stored.
func TestWorkspaceImportReportsDroppedRelationDefaults(t *testing.T) {
const ts = "2026-09-25T12:00:00Z"
const item = "11111111-1111-4111-8111-111111111111"
for _, tc := range []struct{ name, def, want string }{
{"dangling", "33333333-3333-4333-8333-333333333333", "1"},
{"imported", item, "0"},
} {
t.Run(tc.name, func(t *testing.T) {
srv := testServer(t)
bundle := models.WorkspaceExport{
Version: 1,
Workspace: models.WorkspaceExportMeta{Name: "Def " + tc.name, Slug: "def-" + tc.name, Settings: "{}"},
Collections: []models.CollectionExport{{
ID: "c", Name: "Docs", Slug: "docs", Prefix: "DOC", Settings: "{}", CreatedAt: ts, UpdatedAt: ts,
Schema: `{"fields":[{"key":"see","type":"relation","collection":"docs","default":"` + tc.def + `"}]}`,
}},
Items: []models.ItemExport{
{ID: item, CollectionID: "c", Title: "One", Slug: "one", Fields: "{}", Tags: "[]", ItemNumber: 1, CreatedAt: ts, UpdatedAt: ts},
},
}
b, err := json.Marshal(bundle)
if err != nil {
t.Fatal(err)
}
rr := rawJSONRequest(srv, "POST", "/api/v1/workspaces/import", string(b))
if rr.Code != http.StatusCreated {
t.Fatalf("import: %d %s", rr.Code, rr.Body.String())
}
if got := rr.Header().Get(ImportDroppedRelationDefaultsHeader); got != tc.want {
t.Errorf("%s = %q, want %q", ImportDroppedRelationDefaultsHeader, got, tc.want)
}
})
}
}