fix(topdata): close invariant JSON roots
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package topdata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -309,6 +310,59 @@ func TestValidateProjectRejectsUnsupportedCanonicalJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateProjectRejectsUnsupportedInvariantBaseRoots(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dataset string
|
||||
base string
|
||||
lock string
|
||||
}{
|
||||
{
|
||||
name: "feat",
|
||||
dataset: "feat",
|
||||
base: `{
|
||||
"output": "feat.2da",
|
||||
"columns": ["LABEL", "FEAT", "DESCRIPTION"],
|
||||
"rows": [
|
||||
{"id": 0, "key": "feat:test", "LABEL": "FEAT_TEST", "FEAT": "100", "DESCRIPTION": "200"}
|
||||
],
|
||||
"typo": true
|
||||
}` + "\n",
|
||||
lock: `{"feat:test":0}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "masterfeats",
|
||||
dataset: "masterfeats",
|
||||
base: `{
|
||||
"output": "masterfeats.2da",
|
||||
"columns": ["LABEL", "STRREF", "DESCRIPTION"],
|
||||
"rows": [
|
||||
{"id": 0, "key": "masterfeats:test", "LABEL": "Test", "STRREF": "100", "DESCRIPTION": "200"}
|
||||
],
|
||||
"typo": true
|
||||
}` + "\n",
|
||||
lock: `{"masterfeats:test":0}` + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
writeCanonicalContractScaffold(t, root)
|
||||
datasetDir := filepath.Join(root, "topdata", "data", tc.dataset)
|
||||
mkdirAll(t, datasetDir)
|
||||
writeFile(t, filepath.Join(datasetDir, "base.json"), tc.base)
|
||||
writeFile(t, filepath.Join(datasetDir, "lock.json"), tc.lock)
|
||||
|
||||
report := ValidateProject(testProject(root))
|
||||
if !report.HasErrors() {
|
||||
t.Fatalf("expected unsupported invariant base-root key to fail validation, got:\n%s", diagnosticsText(report.Diagnostics))
|
||||
}
|
||||
assertDiagnosticTextContainsAll(t, report.Diagnostics, "base.json root", "typo", "unsupported")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateProjectRejectsUnsupportedGlobalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -339,7 +393,7 @@ func TestValidateProjectRejectsUnsupportedGlobalJSON(t *testing.T) {
|
||||
}
|
||||
]
|
||||
}` + "\n",
|
||||
fragments: []string{"require_present[0]", "typo", "unsupported"},
|
||||
fragments: []string{"global injection 0 require_present[0]", "typo", "unsupported"},
|
||||
},
|
||||
{
|
||||
name: "default rule is closed",
|
||||
@@ -483,7 +537,7 @@ func TestValidateProjectReportsUnsupportedGlobalInjectionKeysAndInvalidAnyPresen
|
||||
}
|
||||
assertDiagnosticTextContainsAll(t, report.Diagnostics,
|
||||
`global injection 0 contains unsupported key "when_present"`,
|
||||
"any_present must contain at least one condition",
|
||||
"global injection 0 any_present must contain at least one condition",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1133,6 +1187,80 @@ func TestBuildNativeRejectsUnsupportedCanonicalRoot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildNativeRejectsUnsupportedInvariantBaseRootsWithoutChangingOutput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dataset string
|
||||
outputName string
|
||||
base string
|
||||
lock string
|
||||
}{
|
||||
{
|
||||
name: "feat",
|
||||
dataset: "feat",
|
||||
outputName: "feat.2da",
|
||||
base: `{
|
||||
"output": "feat.2da",
|
||||
"columns": ["LABEL", "FEAT", "DESCRIPTION"],
|
||||
"rows": [
|
||||
{"id": 0, "key": "feat:test", "LABEL": "FEAT_TEST", "FEAT": "100", "DESCRIPTION": "200"}
|
||||
],
|
||||
"typo": true
|
||||
}` + "\n",
|
||||
lock: `{"feat:test":0}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "masterfeats",
|
||||
dataset: "masterfeats",
|
||||
outputName: "masterfeats.2da",
|
||||
base: `{
|
||||
"output": "masterfeats.2da",
|
||||
"columns": ["LABEL", "STRREF", "DESCRIPTION"],
|
||||
"rows": [
|
||||
{"id": 0, "key": "masterfeats:test", "LABEL": "Test", "STRREF": "100", "DESCRIPTION": "200"}
|
||||
],
|
||||
"typo": true
|
||||
}` + "\n",
|
||||
lock: `{"masterfeats:test":0}` + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
root := testProjectRoot(t)
|
||||
writeCanonicalContractScaffold(t, root)
|
||||
datasetDir := filepath.Join(root, "topdata", "data", tc.dataset)
|
||||
mkdirAll(t, datasetDir)
|
||||
writeFile(t, filepath.Join(datasetDir, "base.json"), tc.base)
|
||||
writeFile(t, filepath.Join(datasetDir, "lock.json"), tc.lock)
|
||||
|
||||
proj := testProject(root)
|
||||
proj.Config.TopData.ReferenceBuilder = ""
|
||||
outputPath := filepath.Join(compiled2DAOutputDir(proj), tc.outputName)
|
||||
mkdirAll(t, filepath.Dir(outputPath))
|
||||
sentinel := []byte("pre-existing output sentinel\n")
|
||||
writeBytes(t, outputPath, sentinel)
|
||||
|
||||
_, err := BuildNativeWithOptions(proj, NativeBuildOptions{BuildWiki: false}, nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected BuildNativeWithOptions to fail")
|
||||
}
|
||||
for _, fragment := range []string{"base.json root", "typo", "unsupported"} {
|
||||
if !strings.Contains(err.Error(), fragment) {
|
||||
t.Fatalf("error %q does not contain %q", err, fragment)
|
||||
}
|
||||
}
|
||||
got, readErr := os.ReadFile(outputPath)
|
||||
if readErr != nil {
|
||||
t.Fatalf("read preserved output: %v", readErr)
|
||||
}
|
||||
if !bytes.Equal(got, sentinel) {
|
||||
t.Fatalf("output changed after invalid build error:\ngot: %q\nwant: %q", got, sentinel)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func tableContainsCellValue(table twoDATable, column, want string) bool {
|
||||
for _, row := range table.rows {
|
||||
if row[column] == want {
|
||||
|
||||
Reference in New Issue
Block a user