← back to Cli Printing Press
fix(cli): populate parent FK in generated UpsertBatch typed-table test fixture (#1253)
9e0fe96c8488f04a81f1de413a55d7244559679b · 2026-05-12 15:39:07 -0700 · Trevin Chow
buildSubResourceTable emits the parent FK column (e.g. domains_id) as
NOT NULL. The generated TestUpsertBatch_Populates<Name>Table fixture only
supplied an id field, so the typed insert failed with
NOT NULL constraint failed: <table>.<parent>_id on a freshly generated
sub-resource CLI. The test template now detects buildSubResourceTable's
parent FK (first NOT NULL non-PK non-data column) and injects a synthetic
value so the typed insert succeeds.
Closes #1063
Files touched
M internal/generator/generator_test.goM internal/generator/templates/store_upsert_batch_test.go.tmpl
Diff
commit 9e0fe96c8488f04a81f1de413a55d7244559679b
Author: Trevin Chow <trevin@trevinchow.com>
Date: Tue May 12 15:39:07 2026 -0700
fix(cli): populate parent FK in generated UpsertBatch typed-table test fixture (#1253)
buildSubResourceTable emits the parent FK column (e.g. domains_id) as
NOT NULL. The generated TestUpsertBatch_Populates<Name>Table fixture only
supplied an id field, so the typed insert failed with
NOT NULL constraint failed: <table>.<parent>_id on a freshly generated
sub-resource CLI. The test template now detects buildSubResourceTable's
parent FK (first NOT NULL non-PK non-data column) and injects a synthetic
value so the typed insert succeeds.
Closes #1063
---
internal/generator/generator_test.go | 59 ++++++++++++++++++++++
.../templates/store_upsert_batch_test.go.tmpl | 18 +++++--
2 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index fb290497..038c6537 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -3076,6 +3076,65 @@ func TestGenerateStoreSubResourceUpsertBindingOrder(t *testing.T) {
"swapped (id, data, synced_at, fk) binding order must not be emitted")
}
+// TestGenerateStoreSubResourceUpsertBatchTestSatisfiesNotNullFK pins the
+// emitted TestUpsertBatch_Populates<Name>Table fixture against the NOT NULL
+// parent FK column declared by buildSubResourceTable. Without an injected
+// FK value, the typed insert fails the NOT NULL constraint on a freshly
+// generated CLI.
+func TestGenerateStoreSubResourceUpsertBatchTestSatisfiesNotNullFK(t *testing.T) {
+ t.Parallel()
+
+ apiSpec := &spec.APISpec{
+ Name: "subres-fk",
+ Version: "0.1.0",
+ BaseURL: "https://api.example.com",
+ Auth: spec.AuthConfig{Type: "none"},
+ Config: spec.ConfigSpec{
+ Format: "toml",
+ Path: "~/.config/subres-fk-pp-cli/config.toml",
+ },
+ Resources: map[string]spec.Resource{
+ "domains": {
+ Description: "Manage domains",
+ Endpoints: map[string]spec.Endpoint{
+ "list": {Method: "GET", Path: "/domains", Description: "List domains"},
+ },
+ SubResources: map[string]spec.Resource{
+ "verify": {
+ Description: "Verify a domain",
+ Endpoints: map[string]spec.Endpoint{
+ "get": {
+ Method: "GET",
+ Path: "/domains/{domainId}/verify",
+ Description: "Get verification status",
+ Params: []spec.Param{{Name: "domainId", Type: "string", Required: true, Positional: true}},
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+ gen := New(apiSpec, outputDir)
+ gen.VisionSet = VisionTemplateSet{Store: true, Sync: true}
+ require.NoError(t, gen.Generate())
+
+ testSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "store", "upsert_batch_test.go"))
+ require.NoError(t, err)
+ // Anchor all three FK injections to the function body so go test -short
+ // (which skips runGoCommand build/test) still gates a regression that
+ // moved the FK into a comment or only one of the three rows.
+ assert.Regexp(t,
+ `(?s)func TestUpsertBatch_PopulatesVerifyTable\(.*?"domains_id":\s*"[^"]+".*?"domains_id":\s*"[^"]+".*?"domains_id":\s*"[^"]+"`,
+ string(testSrc),
+ "TestUpsertBatch_PopulatesVerifyTable fixture must populate the parent FK column declared NOT NULL by buildSubResourceTable in all three items")
+
+ runGoCommand(t, outputDir, "mod", "tidy")
+ runGoCommand(t, outputDir, "test", "./internal/store")
+}
+
func TestGenerateSimilarCommandUsesCompositeResourceKey(t *testing.T) {
t.Parallel()
diff --git a/internal/generator/templates/store_upsert_batch_test.go.tmpl b/internal/generator/templates/store_upsert_batch_test.go.tmpl
index 698a7fd9..919c4c60 100644
--- a/internal/generator/templates/store_upsert_batch_test.go.tmpl
+++ b/internal/generator/templates/store_upsert_batch_test.go.tmpl
@@ -267,8 +267,18 @@ func TestUpsertBatch_ExtractFailuresReturnedForPerItemMisses(t *testing.T) {
{{- if $hasTyped }}
{{- range .Tables}}
{{- if hasTypedTable .}}
+{{- /* $parentFKCol captures buildSubResourceTable's NOT NULL parent FK
+ (e.g. "domains_id"). The fixture must populate it or the typed
+ insert fails the NOT NULL constraint. The predicate is
+ first-match-wins: buildSubResourceTable emits parentCol second
+ (after id, before data/synced_at), so it wins even if a future
+ schema change introduces another NotNull non-PK column. */ -}}
{{- $hasParentID := false }}
-{{- range .Columns}}{{if eq .Name "parent_id"}}{{$hasParentID = true}}{{end}}{{end}}
+{{- $parentFKCol := "" }}
+{{- range .Columns}}
+{{- if eq .Name "parent_id"}}{{$hasParentID = true}}{{end}}
+{{- if and .NotNull (not .PrimaryKey) (ne .Name "data") (eq $parentFKCol "")}}{{$parentFKCol = .Name}}{{end}}
+{{- end}}
// TestUpsertBatch_Populates{{pascal .Name}}Table verifies that UpsertBatch
// dispatches paginated items into both the generic resources table AND the
@@ -284,9 +294,9 @@ func TestUpsertBatch_Populates{{pascal .Name}}Table(t *testing.T) {
defer s.Close()
items := []json.RawMessage{
- json.RawMessage(`{"id": "test-001"}`),
- json.RawMessage(`{"id": "test-002"}`),
- json.RawMessage(`{"id": "test-003"}`),
+ json.RawMessage(`{"id": "test-001"{{if $parentFKCol}}, "{{$parentFKCol}}": "test-parent-001"{{end}}}`),
+ json.RawMessage(`{"id": "test-002"{{if $parentFKCol}}, "{{$parentFKCol}}": "test-parent-001"{{end}}}`),
+ json.RawMessage(`{"id": "test-003"{{if $parentFKCol}}, "{{$parentFKCol}}": "test-parent-001"{{end}}}`),
}
if _, _, err := s.UpsertBatch("{{.Resource}}", items); err != nil {
t.Fatalf("UpsertBatch: %v", err)
← bb9b9cb3 fix(cli): make .printing-press.json authoritative for parsed
·
back to Cli Printing Press
·
chore(main): release 4.5.2 (#1230) 90798403 →