diff --git a/internal/cmd/test/happy-path-validation-file.yaml b/internal/cmd/import-test/happy-path-validation-file.yaml similarity index 100% rename from internal/cmd/test/happy-path-validation-file.yaml rename to internal/cmd/import-test/happy-path-validation-file.yaml diff --git a/internal/cmd/test/happy-path-validation-schema.zed b/internal/cmd/import-test/happy-path-validation-schema.zed similarity index 100% rename from internal/cmd/test/happy-path-validation-schema.zed rename to internal/cmd/import-test/happy-path-validation-schema.zed diff --git a/internal/cmd/import_test.go b/internal/cmd/import_test.go index 8817d147..f23488e3 100644 --- a/internal/cmd/import_test.go +++ b/internal/cmd/import_test.go @@ -24,7 +24,7 @@ func TestImportCmdHappyPath(t *testing.T) { zedtesting.IntFlag{FlagName: "batch-size", FlagValue: 100}, zedtesting.IntFlag{FlagName: "workers", FlagValue: 1}, ) - f := filepath.Join("test", "happy-path-validation-file.yaml") + f := filepath.Join("import-test", "happy-path-validation-file.yaml") // Set up client ctx, cancel := context.WithCancel(context.Background()) diff --git a/internal/cmd/validate-test/external-schema.yaml b/internal/cmd/validate-test/external-schema.yaml new file mode 100644 index 00000000..32b1f43e --- /dev/null +++ b/internal/cmd/validate-test/external-schema.yaml @@ -0,0 +1,9 @@ +--- +schemaFile: "./external-schema.zed" +relationships: >- + resource:1#user@user:1 +assertions: + assertTrue: + - "resource:1#user@user:1" + assertFalse: + - "resource:1#user@user:2" diff --git a/internal/cmd/validate-test/external-schema.zed b/internal/cmd/validate-test/external-schema.zed new file mode 100644 index 00000000..053bff6d --- /dev/null +++ b/internal/cmd/validate-test/external-schema.zed @@ -0,0 +1,6 @@ +definition user {} + +definition resource { + relation user: user + permission view = user +} diff --git a/internal/cmd/validate-test/failed-assertions.yaml b/internal/cmd/validate-test/failed-assertions.yaml new file mode 100644 index 00000000..4da644c4 --- /dev/null +++ b/internal/cmd/validate-test/failed-assertions.yaml @@ -0,0 +1,11 @@ +--- +schema: |- + definition user {} + + definition document { + relation view: user + permission viewer = view + } +assertions: + assertTrue: + - "document:1#viewer@user:maria" diff --git a/internal/cmd/validate-test/invalid-schema.zed b/internal/cmd/validate-test/invalid-schema.zed new file mode 100644 index 00000000..cb7a0c06 --- /dev/null +++ b/internal/cmd/validate-test/invalid-schema.zed @@ -0,0 +1 @@ +something something {} diff --git a/internal/cmd/validate-test/missing-schema.yaml b/internal/cmd/validate-test/missing-schema.yaml new file mode 100644 index 00000000..25913b1a --- /dev/null +++ b/internal/cmd/validate-test/missing-schema.yaml @@ -0,0 +1,8 @@ +--- +relationships: >- + resource:1#user@user:1 +assertions: + assertTrue: + - "resource:1#user@user:1" + assertFalse: + - "resource:1#user@user:2" diff --git a/internal/cmd/validate-test/only-passes-standard.zed b/internal/cmd/validate-test/only-passes-standard.zed new file mode 100644 index 00000000..87044628 --- /dev/null +++ b/internal/cmd/validate-test/only-passes-standard.zed @@ -0,0 +1,2 @@ +// "and" is a reserved keyword in composable schemas +definition and {} diff --git a/internal/cmd/validate-test/schema-only.zed b/internal/cmd/validate-test/schema-only.zed new file mode 100644 index 00000000..053bff6d --- /dev/null +++ b/internal/cmd/validate-test/schema-only.zed @@ -0,0 +1,6 @@ +definition user {} + +definition resource { + relation user: user + permission view = user +} diff --git a/internal/cmd/validate-test/standard-validation.yaml b/internal/cmd/validate-test/standard-validation.yaml new file mode 100644 index 00000000..047b38b8 --- /dev/null +++ b/internal/cmd/validate-test/standard-validation.yaml @@ -0,0 +1,15 @@ +--- +schema: |- + definition user {} + + definition resource { + relation user: user + permission view = user + } +relationships: >- + resource:1#user@user:1 +assertions: + assertTrue: + - "resource:1#user@user:1" + assertFalse: + - "resource:1#user@user:2" diff --git a/internal/cmd/validate_test.go b/internal/cmd/validate_test.go new file mode 100644 index 00000000..1a93f79e --- /dev/null +++ b/internal/cmd/validate_test.go @@ -0,0 +1,110 @@ +package cmd + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + zedtesting "github.com/authzed/zed/internal/testing" +) + +func TestValidate(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + files []string + expectErr string + }{ + `standard_passes`: { + files: []string{ + filepath.Join("validate-test", "standard-validation.yaml"), + }, + }, + `external_schema_passes`: { + files: []string{ + filepath.Join("validate-test", "external-schema.yaml"), + }, + }, + `multiple_files_passes`: { + files: []string{ + filepath.Join("validate-test", "standard-validation.yaml"), + filepath.Join("validate-test", "external-schema.yaml"), + }, + }, + `multiple_files_with_one_failure_fails`: { + files: []string{ + filepath.Join("validate-test", "standard-validation.yaml"), + filepath.Join("validate-test", "invalid-schema.zed"), + }, + expectErr: "Unexpected token at root level", + }, + `schema_only_passes`: { + files: []string{ + filepath.Join("validate-test", "schema-only.zed"), + }, + }, + `invalid_schema_fails`: { + files: []string{ + filepath.Join("validate-test", "invalid-schema.zed"), + }, + expectErr: "Unexpected token at root level", + }, + `standard_only_without_flag_passes`: { + files: []string{ + filepath.Join("validate-test", "only-passes-standard.zed"), + }, + }, + `without_schema_fails`: { + files: []string{ + filepath.Join("validate-test", "missing-schema.yaml"), + }, + expectErr: "either schema or schemaFile must be present", + }, + // TODO capture errors on string and assert on them? + //`assertions_fail`: { + // files: []string{ + // filepath.Join("validate-test", "failed-assertions.yaml"), + // }, + // expectErr: "", + //}, + `invalid_url_fails`: { + files: []string{ + "http://%zz", + }, + expectErr: "invalid URL escape", + }, + `url_does_not_exist_fails`: { + files: []string{ + "https://unknown-url", + }, + expectErr: "Get \"https://unknown-url\": dial tcp: lookup unknown-url", + }, + // TODO: https://github.com/authzed/zed/issues/487 + //`url_passes`: { + // files: []string{ + // "https://play.authzed.com/s/iksdFvCtvnkR/schema", + // }, + //}, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + require := require.New(t) + cmd := zedtesting.CreateTestCobraCommandWithFlagValue(t, + zedtesting.BoolFlag{FlagName: "force-color", FlagValue: false}, + zedtesting.IntFlag{FlagName: "batch-size", FlagValue: 100}, + zedtesting.IntFlag{FlagName: "workers", FlagValue: 1}, + ) + + err := validateCmdFunc(cmd, tc.files) + if tc.expectErr == "" { + require.NoError(err) + } else { + require.ErrorContains(err, tc.expectErr) + } + }) + } +}