oficina / sheets Public

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
Paul Campbell pushed by paul@keymail.dev b4d3e2550960d9553e050cc2b172f9a1008a64e0 parent 3a3febc
1 file changed, +26 −7
  • internal/sheets/models_test.go +26 −7

No CI script — run details

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))