From 69e4f997a52310d97b01c6e0087819d207704ea5 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Fri, 5 Jan 2024 13:05:14 -0500 Subject: [PATCH] fixup: add tests Signed-off-by: Todd Baert --- .github/workflows/pr-checks.yml | 2 +- Makefile | 4 + json/flagd-definitions.json | 4 +- json/flagd-definitions.yaml | 4 +- json/flagd_definitions_test.go | 2 - json/targeting.json | 10 +- json/targeting.yaml | 10 +- .../fractional-invalid-bucketing.json | 26 +++ .../fractional-invalid-weighting.json | 25 +++ .../negative/invalid-ends-with-param.json | 23 +++ json/test/negative/invalid-flagd-props.json | 23 +++ .../negative/invalid-starts-with-param.json | 23 +++ .../sem-ver-invalid-range-specifier.json | 23 +++ .../sem-ver-invalid-ver-expression.json | 23 +++ json/test/positive/custom-ops.json | 158 ++++++++++++++++++ json/test/positive/example.flagd.json | 6 +- 16 files changed, 350 insertions(+), 16 deletions(-) create mode 100644 json/test/negative/fractional-invalid-bucketing.json create mode 100644 json/test/negative/fractional-invalid-weighting.json create mode 100644 json/test/negative/invalid-ends-with-param.json create mode 100644 json/test/negative/invalid-flagd-props.json create mode 100644 json/test/negative/invalid-starts-with-param.json create mode 100644 json/test/negative/sem-ver-invalid-range-specifier.json create mode 100644 json/test/negative/sem-ver-invalid-ver-expression.json create mode 100644 json/test/positive/custom-ops.json diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index a5fd8ce..a479820 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -70,7 +70,7 @@ jobs: with: go-version: ${{ env.GO_VERSION }} - name: Validate Schema - run: go test -v ./json + run: make test # Ensure there is no diff when make gen-schema-json is run - run: make gen-schema-json diff --git a/Makefile b/Makefile index 13e2771..3cfa5cc 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,10 @@ gen-schema-json: install-yq yq eval -o=json json/flagd-definitions.yaml > json/flagd-definitions.json yq eval -o=json json/targeting.yaml > json/targeting.json +.PHONY: test +test: + go test -v ./json + ajv-validate-flagd-schema: @if ! npm ls ajv-cli; then npm ci; fi npx ajv compile -s json/targeting.json diff --git a/json/flagd-definitions.json b/json/flagd-definitions.json index 83fe319..f235259 100644 --- a/json/flagd-definitions.json +++ b/json/flagd-definitions.json @@ -46,7 +46,7 @@ "patternProperties": { "^.{1,}$": { "$comment": "this relative ref means that targeting.json MUST be in the same dir, or available on the same HTTP path", - "$ref": "https://flagd.dev/schema/targeting.json#/$defs/targeting" + "$ref": "./targeting.json#/$defs/targeting" } } } @@ -71,7 +71,7 @@ "type": "string" }, "targeting": { - "$ref": "https://flagd.dev/schema/targeting.json#/$defs/targeting" + "$ref": "./targeting.json#/$defs/targeting" } }, "required": [ diff --git a/json/flagd-definitions.yaml b/json/flagd-definitions.yaml index 9fced8c..a774318 100644 --- a/json/flagd-definitions.yaml +++ b/json/flagd-definitions.yaml @@ -35,7 +35,7 @@ properties: "^.{1,}$": $comment: this relative ref means that targeting.json MUST be in the same dir, or available on the same HTTP path - $ref: "https://flagd.dev/schema/targeting.json#/$defs/targeting" + $ref: "./targeting.json#/$defs/targeting" "$defs": flag: $comment: base flag object; no title/description here, allows for better UX, @@ -56,7 +56,7 @@ properties: if the targeting returns null). type: string targeting: - $ref: "https://flagd.dev/schema/targeting.json#/$defs/targeting" + $ref: "./targeting.json#/$defs/targeting" required: - state - defaultVariant diff --git a/json/flagd_definitions_test.go b/json/flagd_definitions_test.go index 26beefe..df8c690 100644 --- a/json/flagd_definitions_test.go +++ b/json/flagd_definitions_test.go @@ -18,8 +18,6 @@ var s *gojsonschema.Schema func init() { schemaLoader = gojsonschema.NewSchemaLoader() - //gojsonschema.NewStringLoader(flagd_definitions.FlagdDefinitions) - //schemaLoader.AddSchemas() schemaLoader.AddSchemas(gojsonschema.NewStringLoader(flagd_definitions.Targeting)) var err error s, err = schemaLoader.Compile(gojsonschema.NewStringLoader(flagd_definitions.FlagdDefinitions)) diff --git a/json/targeting.json b/json/targeting.json index 255eb97..98db0f5 100644 --- a/json/targeting.json +++ b/json/targeting.json @@ -54,8 +54,12 @@ "pattern": "^\\$flagd\\.((timestamp)|(flagKey))$" }, { - "type": "string", - "pattern": "^(?!\\$flagd.).*$" + "not": { + "$comment": "this is a negated (not) match of \"$flagd.{some-key}\", which is faster and more compatible that a negative lookahead regex", + "type": "string", + "description": "flagd automatically injects \"$flagd.timestamp\" (unix epoch) and \"$flagd.flagKey\" (the key of the flag in evaluation) into the context.", + "pattern": "^\\$flagd\\..*$" + } }, { "type": "array", @@ -513,7 +517,7 @@ ] }, "anyRule": { - "oneOf": [ + "anyOf": [ { "$ref": "#/$defs/varRule" }, diff --git a/json/targeting.yaml b/json/targeting.yaml index 83d3d6a..4a8d4ee 100644 --- a/json/targeting.yaml +++ b/json/targeting.yaml @@ -44,8 +44,12 @@ type: object description: flagd automatically injects "$flagd.timestamp" (unix epoch) and "$flagd.flagKey" (the key of the flag in evaluation) into the context. pattern: "^\\$flagd\\.((timestamp)|(flagKey))$" - - type: string - pattern: "^(?!\\$flagd\\.).+" + - not: + $comment: this is a negated (not) match of "$flagd.{some-key}", which is faster and more compatible that a negative lookahead regex + type: string + description: flagd automatically injects "$flagd.timestamp" (unix epoch) and "$flagd.flagKey" + (the key of the flag in evaluation) into the context. + pattern: "^\\$flagd\\..*$" - type: array $comment: this is to support the form of var with a default... there seems to be a bug here, where ajv gives a warning (not an error) because maxItems doesn't equal the number of entries in items, though this is valid in this case minItems: 1 @@ -393,7 +397,7 @@ type: object - $ref: "#/$defs/anyRule" - $ref: "#/$defs/primitive" anyRule: - oneOf: + anyOf: - $ref: "#/$defs/varRule" - $ref: "#/$defs/missingRule" - $ref: "#/$defs/missingSomeRule" diff --git a/json/test/negative/fractional-invalid-bucketing.json b/json/test/negative/fractional-invalid-bucketing.json new file mode 100644 index 0000000..0bd7b1b --- /dev/null +++ b/json/test/negative/fractional-invalid-bucketing.json @@ -0,0 +1,26 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an invalid bucking value is invalid", + "flags": { + "fractional-invalid-bucketing": { + "state": "ENABLED", + "variants": { + "clubs": "clubs", + "diamonds": "diamonds", + "hearts": "hearts", + "spades": "spades", + "none": "none" + }, + "defaultVariant": "none", + "targeting": { + "fractional": [ + "invalid", + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + } + } + } +} diff --git a/json/test/negative/fractional-invalid-weighting.json b/json/test/negative/fractional-invalid-weighting.json new file mode 100644 index 0000000..92f32c9 --- /dev/null +++ b/json/test/negative/fractional-invalid-weighting.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an invalid weight value is invalid", + "flags": { + "fractional-invalid-weighting": { + "state": "ENABLED", + "variants": { + "clubs": "clubs", + "diamonds": "diamonds", + "hearts": "hearts", + "spades": "spades", + "none": "none" + }, + "defaultVariant": "none", + "targeting": { + "fractional": [ + ["clubs", 25], + ["diamonds", "25"], + ["hearts", 25], + ["spades", 25] + ] + } + } + } +} diff --git a/json/test/negative/invalid-ends-with-param.json b/json/test/negative/invalid-ends-with-param.json new file mode 100644 index 0000000..8901a46 --- /dev/null +++ b/json/test/negative/invalid-ends-with-param.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an an int is not a valid ends_with param", + "flags": { + "invalid-ends-with-param": { + "state": "ENABLED", + "variants": { + "prefix": 1, + "postfix": 2 + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "ends_with": [{ "var": "id" }, 0] + }, + "postfix", + "prefix" + ] + } + } + } +} diff --git a/json/test/negative/invalid-flagd-props.json b/json/test/negative/invalid-flagd-props.json new file mode 100644 index 0000000..7174804 --- /dev/null +++ b/json/test/negative/invalid-flagd-props.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an unsupported $flagd property is invalid", + "flags": { + "invalid-flagd-props": { + "state": "ENABLED", + "variants": { + "yes": "1", + "no": "2" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "==": [{ "var": "$flagd.myprop" }, { "var": "someprop" }] + }, + "yes", + "no" + ] + } + } + } +} diff --git a/json/test/negative/invalid-starts-with-param.json b/json/test/negative/invalid-starts-with-param.json new file mode 100644 index 0000000..6d2fa29 --- /dev/null +++ b/json/test/negative/invalid-starts-with-param.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an an int is not a valid starts_with param", + "flags": { + "invalid-starts-with-param": { + "state": "ENABLED", + "variants": { + "prefix": 1, + "postfix": 2 + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "starts_with": [{ "var": "id" }, 0] + }, + "prefix", + "postfix" + ] + } + } + } +} diff --git a/json/test/negative/sem-ver-invalid-range-specifier.json b/json/test/negative/sem-ver-invalid-range-specifier.json new file mode 100644 index 0000000..d9faf1f --- /dev/null +++ b/json/test/negative/sem-ver-invalid-range-specifier.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an invalid range specifier is invalid", + "flags": { + "sem-ver-invalid-range-specifier": { + "state": "ENABLED", + "variants": { + "equal": "equal", + "not": "not" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "sem_ver": [{ "var": "version" }, "*", "2.0.0"] + }, + "equal", + "not" + ] + } + } + } +} diff --git a/json/test/negative/sem-ver-invalid-ver-expression.json b/json/test/negative/sem-ver-invalid-ver-expression.json new file mode 100644 index 0000000..8804037 --- /dev/null +++ b/json/test/negative/sem-ver-invalid-ver-expression.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that an invalid version is invalid", + "flags": { + "sem-ver-invalid-ver-expression": { + "state": "ENABLED", + "variants": { + "equal": "equal", + "not": "not" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "sem_ver": [{ "var": "version" }, "=", "2.0.0.0"] + }, + "equal", + "not" + ] + } + } + } +} diff --git a/json/test/positive/custom-ops.json b/json/test/positive/custom-ops.json new file mode 100644 index 0000000..4549571 --- /dev/null +++ b/json/test/positive/custom-ops.json @@ -0,0 +1,158 @@ +{ + "$schema": "../../flagd-definitions.json", + "$comments": "tests that all the custom ops work", + "flags": { + "fractional-flag": { + "state": "ENABLED", + "variants": { + "clubs": "clubs", + "diamonds": "diamonds", + "hearts": "hearts", + "spades": "spades", + "wild": "wild" + }, + "defaultVariant": "wild", + "targeting": { + "fractional": [ + { "var": "user.name" }, + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + } + }, + "shorthand-fractional-flag": { + "state": "ENABLED", + "variants": { + "clubs": "clubs", + "diamonds": "diamonds", + "hearts": "hearts", + "spades": "spades", + "wild": "wild" + }, + "defaultVariant": "wild", + "targeting": { + "fractional": [ + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + } + }, + "starts-ends-flag": { + "state": "ENABLED", + "variants": { + "prefix": "prefix", + "postfix": "postfix", + "none": "none" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "starts_with": [{ "var": "id" }, "abc"] + }, + "prefix", + { + "if": [ + { + "ends_with": [{ "var": "id" }, "xyz"] + }, + "postfix", + null + ] + } + ] + } + }, + "equal-greater-lesser-version-flag": { + "state": "ENABLED", + "variants": { + "equal": "equal", + "greater": "greater", + "lesser": "lesser", + "none": "none" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "sem_ver": [{ "var": "version" }, "=", "2.0.0"] + }, + "equal", + { + "if": [ + { + "sem_ver": [{ "var": "version" }, ">", "2.0.0"] + }, + "greater", + { + "if": [ + { + "sem_ver": [{ "var": "version" }, "<", "2.0.0"] + }, + "lesser", + null + ] + } + ] + } + ] + } + }, + "major-minor-version-flag": { + "state": "ENABLED", + "variants": { + "minor": "minor", + "major": "major", + "none": "none" + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + "sem_ver": [{ "var": "version" }, "~", "3.0.0"] + }, + "minor", + { + "if": [ + { + "sem_ver": [{ "var": "version" }, "^", "3.0.0"] + }, + "major", + "none" + ] + } + ] + } + }, + "timestamp-flag": { + "state": "ENABLED", + "variants": { + "past": -1, + "future": 1, + "none": 0 + }, + "defaultVariant": "none", + "targeting": { + "if": [ + { + ">": [{ "var": "$flagd.timestamp" }, { "var": "time" }] + }, + "past", + { + "if": [ + { + "<": [{ "var": "$flagd.timestamp" }, { "var": "time" }] + }, + "future", + "none" + ] + } + ] + } + } + } +} diff --git a/json/test/positive/example.flagd.json b/json/test/positive/example.flagd.json index c7276ce..2a37637 100644 --- a/json/test/positive/example.flagd.json +++ b/json/test/positive/example.flagd.json @@ -98,11 +98,11 @@ "targeting": { "if": [ { - "$ref": 1 + "$ref": "emailWithFaas" }, { - "fractionalEvaluation": [ - "email", + "fractional": [ + { "var": "email" }, ["red", 25], ["blue", 25], ["green", 25],