Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions go/internal/lint/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,93 @@ unknown_prop: true
}
}

func TestLintFile_AcceptsSodaRunner(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "contract.yml")
os.WriteFile(f, []byte(`
dataset: my_ds/db/schema/orders
columns:
- name: id
soda_runner:
checks_schedule:
cron: "0 0 * * *"
timezone: UTC
`), 0644)

result, err := LintFile(f)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !result.Valid {
for _, e := range result.Errors {
t.Logf(" %s: %s", e.Path, e.Message)
}
t.Fatalf("expected valid contract with soda_runner, got %d errors", len(result.Errors))
}
}

func TestLintFile_AcceptsSodaAgentLegacyAlias(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "contract.yml")
os.WriteFile(f, []byte(`
dataset: my_ds/db/schema/orders
columns:
- name: id
soda_agent:
checks_schedule:
cron: "0 0 * * *"
timezone: UTC
`), 0644)

result, err := LintFile(f)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !result.Valid {
for _, e := range result.Errors {
t.Logf(" %s: %s", e.Path, e.Message)
}
t.Fatalf("expected valid contract with deprecated soda_agent, got %d errors", len(result.Errors))
}
}

func TestLintFile_RejectsBothSodaRunnerAndSodaAgent(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "contract.yml")
os.WriteFile(f, []byte(`
dataset: my_ds/db/schema/orders
columns:
- name: id
soda_runner:
checks_schedule:
cron: "0 0 * * *"
soda_agent:
checks_schedule:
cron: "0 0 * * *"
`), 0644)

result, err := LintFile(f)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Valid {
t.Fatal("expected invalid when both soda_runner and soda_agent are set")
}
if len(result.Errors) == 0 {
t.Fatal("expected at least one validation error")
}
rootError := false
for _, e := range result.Errors {
t.Logf(" %s: %s", e.Path, e.Message)
if e.Path == "$" {
rootError = true
}
}
if !rootError {
t.Fatal("expected a validation error at the root path '$' for the not-both constraint")
}
}

func TestSegmentsToPath(t *testing.T) {
tests := []struct {
in []string
Expand Down
37 changes: 34 additions & 3 deletions go/internal/lint/schema/soda_data_contract_json_schema_1_0_0.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
}
},
"soda_agent": {
"description": "The agent configuration to be used within the contract file",
"description": "DEPRECATED: use 'soda_runner' instead. The runner configuration to be used within the contract file. Kept for backwards compatibility.",
"type": "object",
"properties": {
"checks_schedule": {
Expand All @@ -59,8 +59,36 @@
"type": [
"string",
"number"
],
"additionalProperties": false
]
}
}
}
}
}
},
"soda_runner": {
"description": "The runner configuration to be used within the contract file. Replaces 'soda_agent'; both keys are accepted for backwards compatibility but not at the same time.",
"type": "object",
"properties": {
"checks_schedule": {
"type": "object",
"properties": {
"cron": {
"type": "string",
"description": "Cron expression in the format '* * * * *' (minute hour day month weekday). Requires string in quotes."
},
"timezone": {
"type": "string",
"description": "The timezone to be used within the contract file"
},
"variables": {
"type": "object",
"description": "The variables to be used within the contract file",
"additionalProperties": {
"type": [
"string",
"number"
]
}
}
}
Expand Down Expand Up @@ -837,6 +865,9 @@
"dataset",
"columns"
],
"not": {
"required": ["soda_runner", "soda_agent"]
},
"$defs": {
"generic_check_properties": {
"description": "NOT USED YET, problem with inheritance and additionalProperties",
Expand Down