Bring store.Event inside the schema gate
models_test.go's reflection assertion (TestNoPlaintextValueColumns)
walked Sheet, Tab, Row, Col, Cell and Format in package sheets, but
store.Event — the model carrying sealed user content in PayloadSealed —
lives in internal/sheets/store/events.go, outside models.go and outside
the gate's reach. "Schema lives in models.go, nowhere else" was true of
file layout but not of what the gate actually checked: a plaintext-shaped
field added straight to Event passed silently.
Moved the test to package sheets_test so it can import store (store
already imports sheets, so sheets_test -> store -> sheets has no cycle)
and added store.Event{} to the walked model list, rather than moving
Event into models.go. Moving the model would mean renaming store.Event
at every call site across store's production code and tests for a
schema-location tidiness win; the package-based fix closes the actual
gap (Event unchecked) without touching any of Event's existing callers.
Verified live: temporarily added `Value string` to Event, confirmed the
gate fails with "Event.Value is a plaintext-shaped field name", then
reverted (git diff on events.go is clean).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01APtNStXDZ5H2fAc1ZuGqtS
1 file changed,
+26
−7
internal/sheets/models_test.go+26 −7
No CI script — run details
Intent
diff --git a/internal/sheets/models_test.go b/internal/sheets/models_test.go| index 7600b1a..7ec7c65 100644 |
| --- a/internal/sheets/models_test.go |
| +++ b/internal/sheets/models_test.go |
| @@ -1,9 +1,12 @@ |
| -package sheets |
| +package sheets_test |
| import ( |
| "reflect" |
| "strings" |
| "testing" |
| + |
| + "amadan.net/oficina/sheets/internal/sheets" |
| + "amadan.net/oficina/sheets/internal/sheets/store" |
| ) |
| // The models must carry no column that could hold a readable cell value. |
| @@ -21,6 +24,21 @@ import ( |
| // takes. |
| // |
| // Add "Title string" to Sheet and this test fails; that is the point. |
| +// |
| +// This lives in package sheets_test, not package sheets, specifically so |
| +// it can also walk store.Event: Event carries sealed user content |
| +// (PayloadSealed) but is declared in internal/sheets/store/events.go, not |
| +// models.go, so a version of this test confined to package sheets (and |
| +// therefore unable to import store, which itself imports sheets) could |
| +// never see it. Without Event in this walk, "schema lives in models.go, |
| +// nowhere else" was true of the file layout but not of what this gate |
| +// actually checked — someone could add a plaintext-shaped field straight |
| +// to Event and every assertion here would keep passing. Moving Event |
| +// into models.go instead (making sheets.Event) was the other option, but |
| +// it would mean renaming store.Event at every call site across store's |
| +// production code and tests for a schema-location tidiness win the |
| +// package-based fix achieves without touching Event's existing callers |
| +// at all. |
| func TestNoPlaintextValueColumns(t *testing.T) { |
| forbidden := map[string]bool{ |
| "Title": true, |
| @@ -31,12 +49,13 @@ func TestNoPlaintextValueColumns(t *testing.T) { |
| } |
| models := []any{ |
| - Sheet{}, |
| - Tab{}, |
| - Row{}, |
| - Col{}, |
| - Cell{}, |
| - Format{}, |
| + sheets.Sheet{}, |
| + sheets.Tab{}, |
| + sheets.Row{}, |
| + sheets.Col{}, |
| + sheets.Cell{}, |
| + sheets.Format{}, |
| + store.Event{}, |
| } |
| byteSliceType := reflect.TypeOf([]byte(nil)) |