@gspothq/cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -11
- package/configurations/database/postgres/manifest.toml +51 -57
- package/configurations/database/postgres/rules/POSTGRES.md +14 -13
- package/configurations/framework/astro/manifest.toml +33 -35
- package/configurations/framework/astro/rules/ASTRO.md +2 -2
- package/configurations/framework/expo/manifest.toml +19 -19
- package/configurations/framework/express/manifest.toml +10 -4
- package/configurations/framework/express/rules/EXPRESS.md +9 -7
- package/configurations/framework/fastapi/manifest.toml +9 -3
- package/configurations/framework/fastapi/rules/FASTAPI.md +19 -18
- package/configurations/framework/nestjs/manifest.toml +12 -12
- package/configurations/framework/nestjs/rules/NESTJS.md +9 -9
- package/configurations/framework/nextjs/manifest.toml +39 -79
- package/configurations/framework/nextjs/rules/NEXTJS.md +9 -7
- package/configurations/framework/nextjs/tool-files/semgrep/nextjs.yml +35 -0
- package/configurations/framework/react/manifest.toml +33 -43
- package/configurations/framework/react/rules/REACT.md +14 -20
- package/configurations/framework/react-dom/manifest.toml +6 -6
- package/configurations/framework/react-native/manifest.toml +11 -11
- package/configurations/framework/react-native/rules/REACT-NATIVE.md +3 -5
- package/configurations/framework/site/manifest.toml +57 -110
- package/configurations/framework/site/rules/SITE.md +1 -1
- package/configurations/framework/svelte/manifest.toml +29 -31
- package/configurations/framework/svelte/rules/SVELTE.md +6 -6
- package/configurations/framework/vue/manifest.toml +37 -37
- package/configurations/framework/vue/rules/VUE.md +6 -6
- package/configurations/general/commits/manifest.toml +20 -84
- package/configurations/general/commits/rules/GIT.md +24 -13
- package/configurations/general/coverage/manifest.toml +1 -1
- package/configurations/general/dependencies/manifest.toml +47 -107
- package/configurations/general/dependencies/rules/DEPENDENCIES.md +13 -7
- package/configurations/general/docs/manifest.toml +28 -43
- package/configurations/general/docs/rules/DOCS.md +16 -9
- package/configurations/general/duplication/manifest.toml +12 -14
- package/configurations/general/engineering/manifest.toml +6 -2
- package/configurations/general/engineering/rules/agent/PLANNING.md +9 -6
- package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +20 -11
- package/configurations/general/engineering/rules/agent/TASKS.md +6 -2
- package/configurations/general/engineering/rules/agent/WORKING.md +34 -29
- package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +9 -7
- package/configurations/general/engineering/rules/code/CLI.md +12 -9
- package/configurations/general/engineering/rules/code/COMMENTS.md +11 -1
- package/configurations/general/engineering/rules/code/CONFIGURATION.md +2 -0
- package/configurations/general/engineering/rules/code/ERRORS.md +5 -3
- package/configurations/general/engineering/rules/code/HTTP.md +9 -10
- package/configurations/general/engineering/rules/code/LOGGING.md +10 -9
- package/configurations/general/engineering/rules/code/TESTING.md +18 -11
- package/configurations/general/files/manifest.toml +45 -73
- package/configurations/general/files/rules/YAML.md +20 -10
- package/configurations/general/format/manifest.toml +53 -106
- package/configurations/general/gspot/manifest.toml +19 -39
- package/configurations/general/gspot/rules/GENERATED.md +14 -4
- package/configurations/general/licenses/manifest.toml +12 -14
- package/configurations/general/naming/defaults.json +14 -13
- package/configurations/general/naming/manifest.toml +17 -17
- package/configurations/general/naming/rules/NAMING.md +87 -67
- package/configurations/general/prose/manifest.toml +166 -25
- package/configurations/general/prose/rules/WRITING.md +6 -2
- package/configurations/general/repository/manifest.toml +12 -12
- package/configurations/general/secrets/manifest.toml +52 -73
- package/configurations/general/secrets/rules/SECRETS.md +3 -1
- package/configurations/general/security/manifest.toml +29 -52
- package/configurations/general/security/rules/SECURITY.md +8 -6
- package/configurations/general/spelling/manifest.toml +15 -25
- package/configurations/general/structure/manifest.toml +31 -31
- package/configurations/infrastructure/ansible/manifest.toml +15 -15
- package/configurations/infrastructure/docker/manifest.toml +55 -67
- package/configurations/infrastructure/docker/rules/DOCKER.md +15 -13
- package/configurations/infrastructure/github-actions/manifest.toml +21 -41
- package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +16 -12
- package/configurations/infrastructure/github-actions/tool-files/zizmor.yml +5 -0
- package/configurations/infrastructure/nginx/manifest.toml +20 -20
- package/configurations/infrastructure/nginx/rules/NGINX.md +5 -4
- package/configurations/infrastructure/openapi/manifest.toml +20 -20
- package/configurations/infrastructure/openapi/rules/OPENAPI.md +3 -1
- package/configurations/infrastructure/xcode/manifest.toml +55 -75
- package/configurations/infrastructure/xcode/rules/XCODE.md +5 -3
- package/configurations/language/bash/manifest.toml +89 -170
- package/configurations/language/bash/rules/BASH.md +79 -17
- package/configurations/language/bash/rules/LANGUAGE.md +30 -26
- package/configurations/language/bash/rules/OPERATIONS.md +10 -8
- package/configurations/language/bash/rules/SAFETY.md +39 -35
- package/configurations/language/css/manifest.toml +22 -61
- package/configurations/language/css/rules/CSS.md +21 -2
- package/configurations/language/css/rules/TAILWIND.md +7 -5
- package/configurations/language/html/manifest.toml +16 -35
- package/configurations/language/html/rules/HTML.md +23 -5
- package/configurations/language/javascript/eslint-runtime-names.json +66 -61
- package/configurations/language/javascript/eslint-runtime.mjs +118 -0
- package/configurations/language/javascript/manifest.toml +135 -207
- package/configurations/language/javascript/rules/BUN.md +2 -2
- package/configurations/language/javascript/rules/COMPONENTS.md +4 -4
- package/configurations/language/javascript/rules/DENO.md +1 -1
- package/configurations/language/javascript/rules/JAVASCRIPT.md +30 -17
- package/configurations/language/javascript/rules/NODE.md +2 -2
- package/configurations/language/javascript/rules/PLAYWRIGHT.md +10 -9
- package/configurations/language/markdown/manifest.toml +20 -51
- package/configurations/language/python/manifest.toml +231 -342
- package/configurations/language/python/rules/DESIGN.md +40 -33
- package/configurations/language/python/rules/FLOW.md +8 -6
- package/configurations/language/python/rules/PACKAGING.md +14 -7
- package/configurations/language/python/rules/PYTHON.md +83 -33
- package/configurations/language/python/rules/TYPING.md +25 -23
- package/configurations/language/sql/manifest.toml +19 -37
- package/configurations/language/sql/rules/SQL.md +90 -2
- package/configurations/language/swift/manifest.toml +67 -78
- package/configurations/language/swift/rules/SWIFT.md +138 -40
- package/configurations/language/swift/rules/SWIFTUI.md +11 -9
- package/configurations/language/swift/rules/UIKIT.md +21 -19
- package/configurations/language/typescript/manifest.toml +34 -34
- package/configurations/language/typescript/rules/TYPESCRIPT.md +62 -10
- package/configurations/library/drizzle/manifest.toml +25 -38
- package/configurations/library/drizzle/rules/DRIZZLE.md +4 -2
- package/configurations/library/react-hook-form/manifest.toml +4 -4
- package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +15 -13
- package/configurations/library/tanstack-query/manifest.toml +6 -6
- package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +5 -3
- package/configurations/library/testing-library/manifest.toml +6 -6
- package/configurations/library/translations/manifest.toml +22 -22
- package/configurations/library/translations/rules/NEXT-INTL.md +6 -8
- package/configurations/library/translations/rules/TRANSLATIONS.md +22 -23
- package/configurations/library/trpc/manifest.toml +3 -3
- package/configurations/library/trpc/rules/TRPC.md +8 -6
- package/configurations/library/zod/manifest.toml +6 -6
- package/configurations/library/zod/rules/ZOD.md +4 -2
- package/configurations/library/zustand/manifest.toml +4 -4
- package/configurations/library/zustand/rules/ZUSTAND.md +16 -14
- package/configurations/platform/cloudflare/manifest.toml +28 -47
- package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +11 -6
- package/configurations/platform/supabase/manifest.toml +48 -61
- package/configurations/platform/supabase/rules/SUPABASE.md +27 -28
- package/configurations/testing/jest/manifest.toml +21 -22
- package/configurations/testing/pytest/manifest.toml +28 -28
- package/configurations/testing/swift-tests/manifest.toml +18 -29
- package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +7 -5
- package/configurations/testing/vitest/manifest.toml +32 -32
- package/configurations/testing/vitest/rules/VITEST.md +3 -1
- package/dist/gspot.js +22297 -24707
- package/dist/{linkinator-program.js → linkinator-process.js} +5 -7
- package/dist/{purgecss-program.js → purgecss-process.js} +12 -12
- package/package.json +7 -9
- package/configurations/infrastructure/github-actions/zizmor.yml +0 -3
- package/configurations/language/bash/rules/NAMING.md +0 -62
- package/configurations/language/css/rules/NAMING.md +0 -17
- package/configurations/language/html/rules/NAMING.md +0 -16
- package/configurations/language/javascript/rules/NAMING.md +0 -31
- package/configurations/language/markdown/markdownlint-cli2.mjs +0 -19
- package/configurations/language/python/rules/NAMING.md +0 -53
- package/configurations/language/sql/rules/NAMING.md +0 -90
- package/configurations/language/swift/rules/NAMING.md +0 -88
- package/configurations/language/typescript/rules/NAMING.md +0 -73
- package/configurations/library/swift-snapshot-testing/manifest.toml +0 -32
- package/dist/THIRD-PARTY-NOTICES.md +0 -25
- package/dist/eslint-rule-levels.json +0 -10
- package/dist/tool-versions.json +0 -3
- package/gspot.schema.json +0 -45991
- /package/{dist/LICENSE.md → LICENSE.md} +0 -0
- /package/configurations/framework/express/{semgrep → tool-files/semgrep}/express.yml +0 -0
- /package/configurations/framework/fastapi/{semgrep → tool-files/semgrep}/fastapi.yml +0 -0
- /package/configurations/general/dependencies/{syncpack.json → tool-files/syncpack.json} +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/LICENSES.md +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ampm.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/conscious-language.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/contractions.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/currency.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dashes.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/date-ordinals.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dates.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/file-paths.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/future.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/hedging.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/history.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/idioms.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/interface-verbs.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/jargon.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/leading-conjunctions.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/link-text.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/locations.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/marketing.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/merge-conflict-markers.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/militaristic-language.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/modals.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/numbered-units.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/obvious-fields.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ordinal-adverbs.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/percentage-numbers.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/placeholders.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/product-centric-writing.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/punctuated-headings.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/self-reference.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/session-identifiers.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/since.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/straight-quotes.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/suspended-hyphens.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/symbols.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/time-ranges.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/url-prepositions.yml +0 -0
- /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/version-range.yml +0 -0
- /package/configurations/general/secrets/{gitleaks.toml → tool-files/gitleaks.toml} +0 -0
- /package/configurations/infrastructure/docker/{trivyignore → tool-files/trivyignore} +0 -0
- /package/configurations/language/bash/{semgrep → tool-files/semgrep}/bash.yml +0 -0
- /package/configurations/language/javascript/{semgrep → tool-files/semgrep}/javascript.yml +0 -0
- /package/configurations/language/swift/{semgrep → tool-files/semgrep}/swift.yml +0 -0
- /package/configurations/platform/cloudflare/{semgrep → tool-files/semgrep}/cloudflare.yml +0 -0
- /package/configurations/platform/supabase/{semgrep → tool-files/semgrep}/supabase.yml +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
allowed_words = ["Supabase"]
|
|
2
|
+
|
|
2
3
|
[configuration]
|
|
3
|
-
|
|
4
|
+
description = "A Supabase project: edge functions under Deno. Every storage bucket has a policy, and privileged keys stay in their allowed files."
|
|
4
5
|
requires = ["postgres", "javascript"]
|
|
5
6
|
suggests = ["typescript"]
|
|
6
|
-
|
|
7
|
+
title = "Supabase"
|
|
7
8
|
|
|
8
9
|
[detect]
|
|
9
10
|
paths = ["**/supabase/config.toml"]
|
|
@@ -12,101 +13,87 @@ project_files = ["supabase/config.toml"]
|
|
|
12
13
|
[files]
|
|
13
14
|
paths = ["supabase/config.toml", "supabase/functions/**", "supabase/seed.sql"]
|
|
14
15
|
|
|
16
|
+
[defaults]
|
|
17
|
+
"postgres.client_schemas" = ["public"]
|
|
18
|
+
"tools.squawk.assume_in_transaction" = true
|
|
19
|
+
|
|
15
20
|
[[tool]]
|
|
16
|
-
name = "deno"
|
|
17
|
-
version = "2.6.6"
|
|
18
|
-
min_version = "2.0.0"
|
|
19
|
-
mise = "deno"
|
|
20
21
|
brew = "deno"
|
|
21
22
|
github = { name = "denoland/deno", version = "v2.6.6" }
|
|
23
|
+
min_version = "2.0.0"
|
|
24
|
+
mise = "deno"
|
|
25
|
+
name = "deno"
|
|
22
26
|
scoop = "deno"
|
|
27
|
+
version = "2.6.6"
|
|
23
28
|
|
|
24
29
|
[[tool]]
|
|
25
|
-
|
|
26
|
-
system = true
|
|
27
|
-
version_command = ["--version"]
|
|
30
|
+
brew = "supabase/tap/supabase"
|
|
28
31
|
min_version = "2.0.0"
|
|
29
32
|
mise = "supabase"
|
|
30
|
-
|
|
33
|
+
name = "supabase"
|
|
31
34
|
npm = "supabase"
|
|
32
35
|
scoop = "supabase"
|
|
36
|
+
system = true
|
|
37
|
+
version_command = ["--version"]
|
|
33
38
|
|
|
34
|
-
[[
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
runs = "scope"
|
|
40
|
-
summary = "Checks that the project file parses, and that every function it configures has a folder with an index file."
|
|
41
|
-
why = "The CLI reads this file at every deploy, and a function it configures with no code fails there."
|
|
42
|
-
help = "Correct the file at the place the message names, or remove the table of a function the repository does not hold."
|
|
39
|
+
[[tool_file]]
|
|
40
|
+
per_scope = true
|
|
41
|
+
target = ".gspot/config/semgrep/supabase.yml"
|
|
42
|
+
tool = "semgrep"
|
|
43
|
+
when = { configuration = "security" }
|
|
43
44
|
|
|
44
45
|
[[check]]
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
example = "A storage bucket with no policy on `storage.objects` reports a finding. Add a migration that defines access for that bucket."
|
|
47
|
+
help = "Add a policy on storage.objects for the bucket in a migration."
|
|
47
48
|
level = "recommended"
|
|
48
|
-
stage = "commit"
|
|
49
|
-
runs = "scope"
|
|
50
|
-
summary = "Checks that every migration is named fourteen digits, an underscore, and snake case words."
|
|
51
|
-
why = "The CLI orders and records migrations by this name, and it skips a file it cannot read a version from."
|
|
52
|
-
help = "Create the migration with supabase migration new `<name>`, which writes the name."
|
|
53
|
-
|
|
54
|
-
[[check]]
|
|
55
49
|
name = "storage-policies"
|
|
56
|
-
example = "A declared storage bucket with no matching policy on `storage.objects` reports missing access policy. Add a migration defining the intended access policy for that bucket and rerun."
|
|
57
|
-
level = "recommended"
|
|
58
|
-
stage = "commit"
|
|
59
50
|
runs = "scope"
|
|
51
|
+
stage = "commit"
|
|
60
52
|
summary = "Checks that every storage bucket in the project file has a policy on storage.objects that names it."
|
|
61
|
-
why = "A bucket with no policy
|
|
62
|
-
help = "Add a policy on storage.objects for the bucket in a migration."
|
|
53
|
+
why = "A bucket with no policy stays closed to every user, or open to anyone when public, and nobody recorded that choice."
|
|
63
54
|
|
|
64
55
|
[[check]]
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
example = "Client code that reads a Supabase service-role key reports a finding. Move the key and the privileged work into an edge function."
|
|
57
|
+
help = "Move code to edge functions or tests, or run `gspot ignore supabase/service-role-key` with a reason."
|
|
67
58
|
level = "recommended"
|
|
68
|
-
|
|
59
|
+
name = "service-role-key"
|
|
69
60
|
runs = "scope"
|
|
70
|
-
|
|
61
|
+
stage = "commit"
|
|
62
|
+
summary = "Checks that only edge functions and test files name a secret or service role key."
|
|
71
63
|
why = "Secret and service role keys bypass row level security, so a client bundle that holds either exposes privileged database access."
|
|
72
|
-
help = "Move code to edge functions or tests, or run gspot ignore supabase/service-role-key with a reason."
|
|
73
64
|
|
|
74
65
|
[[check]]
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
example = "An Edge Function declaring `var greeting = \"hello\";` reports `no-var`. Use `const greeting = \"hello\";` instead."
|
|
67
|
+
help = "Change the code the way the rule says; `deno lint --rules` explains each one."
|
|
77
68
|
level = "recommended"
|
|
78
|
-
|
|
69
|
+
name = "deno-lint"
|
|
79
70
|
runs = "scope"
|
|
71
|
+
stage = "commit"
|
|
72
|
+
summary = "Runs `deno lint` over every edge function with its own deno.json."
|
|
80
73
|
tool = "deno"
|
|
81
|
-
|
|
82
|
-
why = "Edge functions run under Deno, and its linter knows the runtime the Node rules do not."
|
|
83
|
-
help = "Change the code the way the rule says; deno lint --rules explains each one."
|
|
74
|
+
why = "Edge functions run under Deno, and its linter covers runtime APIs that the Node rules miss."
|
|
84
75
|
|
|
85
76
|
[[check]]
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
example = "An edge function that assigns a string to a number variable reports a type error. Correct the value or the type."
|
|
78
|
+
help = "Fix the first error Deno prints; later ones often come from the same mistake."
|
|
88
79
|
level = "recommended"
|
|
89
|
-
|
|
80
|
+
name = "deno-check"
|
|
90
81
|
runs = "scope"
|
|
82
|
+
stage = "push"
|
|
83
|
+
summary = "Runs `deno check` on the entry file of every edge function."
|
|
91
84
|
tool = "deno"
|
|
92
|
-
|
|
93
|
-
why = "The project compiler does not resolve the import map of a function, so only Deno sees its types."
|
|
94
|
-
help = "Fix the first error Deno prints; later ones are often the same mistake."
|
|
85
|
+
why = "The project compiler cannot resolve the import map of a function, so only Deno can check its types."
|
|
95
86
|
|
|
96
87
|
[[check]]
|
|
97
|
-
name = "stale-types"
|
|
98
88
|
command = ["supabase", "gen", "types", "typescript", "--local", "{each:--schema:supabase.schemas}"]
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
example = "A types file that differs from the local Supabase schema reports drift. With the local database running, write the output of `supabase gen types typescript --local` to that file."
|
|
90
|
+
generator_outputs = [{ stdout = "{setting:supabase.types_file}" }]
|
|
91
|
+
help = "Run `supabase gen types typescript --local` and write the output to the file."
|
|
101
92
|
level = "recommended"
|
|
102
|
-
|
|
93
|
+
name = "stale-types"
|
|
94
|
+
needs = ["docker"]
|
|
103
95
|
runs = "scope"
|
|
96
|
+
stage = "push"
|
|
97
|
+
summary = "Compares `supabase.types_file` with the output the Supabase CLI writes from the local database."
|
|
104
98
|
tool = "supabase"
|
|
105
|
-
needs = ["docker"]
|
|
106
|
-
summary = "Compares supabase.types_file with the types the CLI writes from the local database."
|
|
107
99
|
why = "Stale types let code compile against a column the database dropped."
|
|
108
|
-
help = "Run supabase gen types typescript --local and write the output to the file."
|
|
109
|
-
|
|
110
|
-
[defaults]
|
|
111
|
-
"postgres.client_schemas" = ["public"]
|
|
112
|
-
"tools.squawk.assume_in_transaction" = true
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Supabase
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Read this rule when you change Supabase migrations, storage, Edge Functions, or `config.toml`.
|
|
4
|
+
|
|
5
|
+
Follow the Postgres rules too. Supabase uses the API roles `anon`, `authenticated`, and
|
|
5
6
|
`service_role`.
|
|
6
7
|
|
|
7
8
|
## Layout and migrations
|
|
@@ -11,13 +12,12 @@ project, on top of the Postgres rules. Supabase uses the API roles `anon`, `auth
|
|
|
11
12
|
|
|
12
13
|
## Database access
|
|
13
14
|
|
|
14
|
-
- `[api].schemas` in `config.toml` lists the schemas the Data API exposes. Treat
|
|
15
|
+
- `[api].schemas` in `config.toml` lists the schemas the Data API exposes. Treat them as
|
|
15
16
|
externally reachable.
|
|
16
|
-
- row level security (RLS)
|
|
17
|
-
must not read.
|
|
17
|
+
- A table with row level security (RLS) on and no policy denies access to the API roles. Use that choice for tables
|
|
18
|
+
that clients must not read.
|
|
18
19
|
- Roles Supabase creates for auth, cron, or storage stay scoped to the functions and schemas they need.
|
|
19
|
-
-
|
|
20
|
-
role never stands in for precise grants or hides a missing policy.
|
|
20
|
+
- Name the Supabase roles in `TO`: `anon`, `authenticated`, or `service_role`.
|
|
21
21
|
- A table can skip the foreign key to `auth.users` so its history survives account deletion.
|
|
22
22
|
- Keep `[api].max_rows` conservative to bound accidental or malicious payloads.
|
|
23
23
|
|
|
@@ -25,17 +25,16 @@ project, on top of the Postgres rules. Supabase uses the API roles `anon`, `auth
|
|
|
25
25
|
|
|
26
26
|
Choose the Supabase connection mode for the workload.
|
|
27
27
|
|
|
28
|
-
| Workload
|
|
29
|
-
|
|
|
30
|
-
| Short-lived serverless application requests
|
|
31
|
-
| Persistent server with direct network access
|
|
32
|
-
| Persistent server on an IPv4-only network
|
|
33
|
-
| Migrations, dumps, or session-dependent
|
|
34
|
-
|
|
35
|
-
A separate migration credential holds schema privileges. The application role holds only
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
uses the transaction pooler's supported configuration.
|
|
28
|
+
| Workload | Connection choice |
|
|
29
|
+
| --------------------------------------------- | --------------------------------------------------------------------- |
|
|
30
|
+
| Short-lived serverless application requests | Transaction pooler, with a compatible driver |
|
|
31
|
+
| Persistent server with direct network access | Direct connection with a bounded application pool |
|
|
32
|
+
| Persistent server on an IPv4-only network | Session pooler when the server cannot reach the direct endpoint |
|
|
33
|
+
| Migrations, dumps, or session-dependent tools | Direct connection; use a supported session alternative when necessary |
|
|
34
|
+
|
|
35
|
+
A separate migration credential holds schema privileges. The application role holds only the privileges it needs at runtime.
|
|
36
|
+
See [connection modes](https://supabase.com/docs/guides/database/connecting-to-postgres).
|
|
37
|
+
|
|
39
38
|
Set explicit connection, idle, and query timeouts. Budget connections across instances and
|
|
40
39
|
workers. With Postgres.js through transaction pooling, set `prepare: false` and confirm the setting
|
|
41
40
|
after a driver or pooler change.
|
|
@@ -46,16 +45,16 @@ maps the identity to a fixed set of allowed roles, then opens a transaction on a
|
|
|
46
45
|
connection. Use parameterized transaction-local `set_config` for claims and fixed trusted SQL for
|
|
47
46
|
the role. Every protected query uses that transaction. Commit or rollback ends its context.
|
|
48
47
|
|
|
49
|
-
When Drizzle generates migrations that the Supabase CLI applies, keep the CLI
|
|
48
|
+
When Drizzle generates migrations that the Supabase CLI applies, keep the CLI migration format
|
|
50
49
|
and one authoritative applied-history ledger. Declare provider-managed schemas and roles as
|
|
51
|
-
existing. Check replay
|
|
50
|
+
existing. Check replay with the project migration tool. See
|
|
52
51
|
[migration history](https://supabase.com/docs/guides/local-development/database-migrations) and
|
|
53
52
|
[verified claims](https://supabase.com/docs/reference/javascript/auth-getclaims).
|
|
54
53
|
|
|
55
54
|
## Storage
|
|
56
55
|
|
|
57
56
|
- Define buckets in migrations and mirror them in `config.toml`. A change to `public`,
|
|
58
|
-
`file_size_limit`, or `allowed_mime_types` updates both
|
|
57
|
+
`file_size_limit`, or `allowed_mime_types` updates both at once.
|
|
59
58
|
- Bucket restrictions enforce file size and MIME type, not only application validation.
|
|
60
59
|
- RLS policies on `storage.objects` and `storage.buckets` control access. The service controls metadata in the `storage` schema: migrations add only policies, indexes for RLS or validation, triggers,
|
|
61
60
|
and bucket configuration. Object rows change only through the Storage API.
|
|
@@ -79,15 +78,15 @@ existing. Check replay through the chosen runner. See
|
|
|
79
78
|
|
|
80
79
|
## Edge functions
|
|
81
80
|
|
|
82
|
-
- Each deployable function lives under `functions/<function-name>/` in the CLI
|
|
83
|
-
with a `[functions.<name>]`
|
|
81
|
+
- Each deployable function lives under `functions/<function-name>/` in the CLI functions root,
|
|
82
|
+
with a `[functions.<name>]` table whose `entrypoint` path starts from the folder of `config.toml`.
|
|
84
83
|
Shared code lives in `functions/_shared/`, which Supabase never deploys.
|
|
85
84
|
- Name a function folder in kebab-case for its callable operation, not its technology:
|
|
86
|
-
`submit-order`, never `SubmitOrder` or `provider_handler`. The folder,
|
|
87
|
-
deployed name match.
|
|
85
|
+
`submit-order`, never `SubmitOrder` or `provider_handler`. The folder, the `config.toml` table, and
|
|
86
|
+
the deployed name match.
|
|
88
87
|
- Edge Functions run on Deno, and the Deno rules hold.
|
|
89
88
|
- Keep `verify_jwt = true` for user-authenticated functions. Set it to `false` only when the function
|
|
90
|
-
exposes
|
|
89
|
+
exposes nothing but public, harmless behavior, verifies a provider webhook signature, checks a service secret, or takes
|
|
91
90
|
a Vault secret from cron.
|
|
92
91
|
- A publishable or secret API key differs from a user JSON Web Token (JWT). Never send that key as
|
|
93
92
|
`Authorization: Bearer <key>`. Forward the caller's `Authorization` header to the Supabase
|
|
@@ -99,7 +98,7 @@ existing. Check replay through the chosen runner. See
|
|
|
99
98
|
in `functions/_shared/`.
|
|
100
99
|
- Functions have runtime, CPU, memory, bundle-size, and log-rate limits, so long CPU-heavy work
|
|
101
100
|
runs elsewhere. Raw Supabase errors that expose credentials stay out of logs.
|
|
102
|
-
- When you write tests, cover function logic
|
|
101
|
+
- When you write tests, cover function logic at the unit level. Integration tests against a local
|
|
103
102
|
database and function server cover the client, auth, RLS, and the invocation path.
|
|
104
103
|
|
|
105
104
|
## Configuration and secrets
|
|
@@ -108,7 +107,7 @@ existing. Check replay through the chosen runner. See
|
|
|
108
107
|
|
|
109
108
|
Ask the user before changing signup, anonymous sign-in, providers, hooks, JWT expiry, or password policy.
|
|
110
109
|
Also ask before weakening auth settings, row-level security, grants, or `verify_jwt`.
|
|
111
|
-
Proceed when the task already authorizes the change.
|
|
110
|
+
Proceed when the task already authorizes the change. When the user authorizes a commit, write the reason in it.
|
|
112
111
|
|
|
113
112
|
- Service keys, Edge secrets, Vault values, database URLs, and provider API keys stay backend-only.
|
|
114
113
|
They never appear in app code, checked-in files, logs, migration comments, tests, or test data.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[configuration]
|
|
2
|
-
|
|
2
|
+
description = "Jest lint rules and coverage floors for the repository test suites."
|
|
3
3
|
requires = ["javascript", "coverage"]
|
|
4
|
-
|
|
4
|
+
title = "Jest"
|
|
5
5
|
|
|
6
6
|
[detect]
|
|
7
7
|
dependencies = ["jest"]
|
|
@@ -15,27 +15,35 @@ name = "jest"
|
|
|
15
15
|
system = true
|
|
16
16
|
|
|
17
17
|
[[tool]]
|
|
18
|
+
kind = "library"
|
|
18
19
|
name = "eslint-plugin-jest"
|
|
20
|
+
npm = "eslint-plugin-jest"
|
|
19
21
|
rule_prefix = "jest"
|
|
20
|
-
kind = "library"
|
|
21
22
|
version = "29.16.6"
|
|
22
|
-
|
|
23
|
+
|
|
24
|
+
[eslint]
|
|
25
|
+
all_level_rules = ["jest/prefer-strict-equal"]
|
|
26
|
+
|
|
27
|
+
[[tool_file]]
|
|
28
|
+
fragment = true
|
|
29
|
+
required_tools = ["eslint-plugin-jest"]
|
|
30
|
+
target = ".gspot/config/eslint.config.mjs"
|
|
31
|
+
tool = "eslint"
|
|
23
32
|
|
|
24
33
|
[[check]]
|
|
25
|
-
|
|
34
|
+
command = ["jest", "--ci", "--rootDir={root}/{scope}", "--coverage", "--coverageReporters=text-summary", "--coverageThreshold={\"global\":{\"lines\":{setting:coverage.lines},\"branches\":{setting:coverage.branches},\"functions\":{setting:coverage.functions},\"statements\":{setting:coverage.statements}}}"]
|
|
35
|
+
crash_pattern = "^No tests found|^ ● Test suite failed to run"
|
|
36
|
+
cwd = "scope"
|
|
26
37
|
example = "Jest reports `coverage threshold for lines (90%) not met: 85%`. Add tests for the lines its report marks uncovered."
|
|
38
|
+
exit_codes = [1]
|
|
39
|
+
help = "Correct failed tests and cover the reported branches, functions, lines, and statements."
|
|
27
40
|
level = "recommended"
|
|
28
|
-
|
|
41
|
+
name = "coverage"
|
|
29
42
|
runs = "scope"
|
|
30
|
-
|
|
43
|
+
stage = "push"
|
|
44
|
+
summary = "Runs Jest and checks all four coverage floors."
|
|
31
45
|
tool = "jest"
|
|
32
|
-
command = ["jest", "--ci", "--rootDir={root}/{scope}", "--coverage", "--coverageReporters=text-summary", "--coverageThreshold={\"global\":{\"lines\":{setting:coverage.lines},\"branches\":{setting:coverage.branches},\"functions\":{setting:coverage.functions},\"statements\":{setting:coverage.statements}}}"]
|
|
33
|
-
run_in_copy = true
|
|
34
|
-
exit_codes = [1]
|
|
35
|
-
crash_pattern = "^No tests found|^ ● Test suite failed to run"
|
|
36
|
-
summary = "Runs Jest in a disposable source copy and checks all four coverage floors."
|
|
37
46
|
why = "A coverage floor keeps new code from arriving untested."
|
|
38
|
-
help = "Correct failed tests and cover the reported branches, functions, lines, and statements."
|
|
39
47
|
|
|
40
48
|
[check.files]
|
|
41
49
|
extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
@@ -43,12 +51,3 @@ extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]
|
|
|
43
51
|
[check.output]
|
|
44
52
|
format = "regex"
|
|
45
53
|
pattern = "^Jest: \"global\" (?<message>coverage threshold for .+ not met: .+)$"
|
|
46
|
-
|
|
47
|
-
[eslint]
|
|
48
|
-
all_level_rules = ["jest/prefer-strict-equal"]
|
|
49
|
-
|
|
50
|
-
[[tool_file]]
|
|
51
|
-
target = ".gspot/config/eslint.config.mjs"
|
|
52
|
-
tool = "eslint"
|
|
53
|
-
fragment = true
|
|
54
|
-
required_tools = ["eslint-plugin-jest"]
|
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
[configuration]
|
|
2
|
-
|
|
2
|
+
description = "pytest suites: Ruff style rules for tests, test-name prefixes, and a coverage floor at push."
|
|
3
3
|
requires = ["python", "coverage"]
|
|
4
|
-
|
|
4
|
+
title = "pytest"
|
|
5
5
|
|
|
6
6
|
[detect]
|
|
7
7
|
dependencies = ["pytest"]
|
|
8
8
|
filenames = ["conftest.py", "pytest.ini"]
|
|
9
9
|
|
|
10
|
+
[[naming.override]]
|
|
11
|
+
categories = ["functions", "methods"]
|
|
12
|
+
ignored_prefix = "^test_"
|
|
13
|
+
languages = ["python"]
|
|
14
|
+
paths = ["**/test_*.py", "**/*_test.py", "**/tests/**/*.py", "**/conftest.py"]
|
|
15
|
+
reason = "The test runner finds a test by this prefix."
|
|
16
|
+
|
|
10
17
|
[[tool]]
|
|
11
|
-
name = "pytest"
|
|
12
18
|
install_hint = "Run: python -m pip install pytest"
|
|
13
|
-
system = true
|
|
14
|
-
suppression = { marker = "\\bpragma: ?no cover\\b", reason = "#[^#]*# ?(?<reason>\\S.*)" }
|
|
15
19
|
min_version = "7.0.0"
|
|
20
|
+
name = "pytest"
|
|
16
21
|
pypi = "pytest"
|
|
22
|
+
suppression = { marker = "\\bpragma: ?no cover\\b", reason = "#[^#]*# ?(?<reason>\\S.*)" }
|
|
23
|
+
system = true
|
|
17
24
|
version_pattern = "(\\d+\\.\\d+\\.\\d+)"
|
|
18
25
|
|
|
19
26
|
[[tool]]
|
|
20
|
-
name = "pytest-cov"
|
|
21
27
|
install_hint = "Install pytest-cov in the Python environment used by pytest."
|
|
22
28
|
kind = "library"
|
|
23
|
-
|
|
29
|
+
name = "pytest-cov"
|
|
24
30
|
pypi = "pytest-cov"
|
|
31
|
+
system = true
|
|
25
32
|
version_command = ["pytest", "--version", "--version"]
|
|
26
33
|
version_pattern = "pytest-cov-(\\d+(?:\\.\\d+)+)"
|
|
27
34
|
|
|
35
|
+
[ruff]
|
|
36
|
+
all = ["PT001", "PT002", "PT003", "PT006", "PT007", "PT018", "PT019", "PT022", "PT023", "PT026"]
|
|
37
|
+
recommended = ["PT008", "PT009", "PT010", "PT011", "PT012", "PT013", "PT014", "PT015", "PT016", "PT017", "PT020", "PT021", "PT024", "PT025", "PT027", "PT028", "PT030", "PT031"]
|
|
38
|
+
|
|
28
39
|
[[check]]
|
|
29
|
-
name = "coverage"
|
|
30
|
-
tool = ["pytest", "pytest-cov"]
|
|
31
|
-
example = "pytest-cov reports `FAIL Required test coverage of 90% not reached. Total coverage: 85%`. Add a Python test for the uncovered behavior."
|
|
32
|
-
level = "recommended"
|
|
33
|
-
stage = "push"
|
|
34
|
-
runs = "scope"
|
|
35
|
-
cwd = "scope"
|
|
36
40
|
command = ["pytest", "-q", "--strict-markers", "--strict-config", "--cov", "--cov-fail-under={setting:coverage.lines}", "--no-header", "-p", "no:cacheprovider"]
|
|
41
|
+
cwd = "scope"
|
|
42
|
+
example = "pytest-cov reports `FAIL Required test coverage of 90% not reached. Total coverage: 85%`. Add a Python test for the uncovered behavior."
|
|
37
43
|
exit_codes = [1]
|
|
38
44
|
files = { extensions = [".py"] }
|
|
45
|
+
help = "Fix the failed test, or cover the lines the report marks. Lower `coverage.lines` only with a reason."
|
|
46
|
+
level = "recommended"
|
|
47
|
+
name = "coverage"
|
|
39
48
|
output = { format = "regex", pattern = "^(?<message>(?:FAIL Required test coverage|FAILED |ERROR ).*)$" }
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
recommended = ["PT008", "PT009", "PT010", "PT011", "PT012", "PT013", "PT014", "PT015", "PT016", "PT017", "PT020", "PT021", "PT024", "PT025", "PT027", "PT028", "PT030", "PT031"]
|
|
46
|
-
all = ["PT001", "PT002", "PT003", "PT006", "PT007", "PT018", "PT019", "PT022", "PT023", "PT026"]
|
|
47
|
-
|
|
48
|
-
[[naming.override]]
|
|
49
|
-
paths = ["**/test_*.py", "**/*_test.py", "**/tests/**/*.py", "**/conftest.py"]
|
|
50
|
-
languages = ["python"]
|
|
51
|
-
categories = ["functions", "methods"]
|
|
52
|
-
ignored_prefix = "^test_"
|
|
53
|
-
reason = "The test runner finds a test by this prefix."
|
|
49
|
+
runs = "scope"
|
|
50
|
+
stage = "push"
|
|
51
|
+
summary = "Runs pytest with coverage, and fails on a broken test or a covered share under the floor."
|
|
52
|
+
tool = ["pytest", "pytest-cov"]
|
|
53
|
+
why = "Coverage shows the Python branches and lines that no test runs, before a push."
|
|
@@ -1,52 +1,41 @@
|
|
|
1
1
|
[configuration]
|
|
2
|
-
|
|
2
|
+
description = "Swift test files under XCTest and Swift Testing. A disabled test says why, and coverage holds a floor."
|
|
3
3
|
requires = ["swift", "coverage"]
|
|
4
|
-
|
|
4
|
+
title = "Swift Tests"
|
|
5
5
|
|
|
6
6
|
[detect]
|
|
7
|
-
tags = ["swift-test", "swift-test-target"]
|
|
8
7
|
paths = ["**/*.xctestplan"]
|
|
8
|
+
tags = ["swift-test", "swift-test-target"]
|
|
9
9
|
|
|
10
10
|
[files]
|
|
11
|
-
paths = ["**/*Tests/**/*.swift"]
|
|
12
11
|
filenames = ["*.xctestplan"]
|
|
12
|
+
paths = ["**/*Tests/**/*.swift"]
|
|
13
13
|
|
|
14
14
|
[[tool_file]]
|
|
15
|
+
per_scope = true
|
|
16
|
+
pointer = { path = ".swiftlint.yml", folders = ["**/*Tests"], body = "parent_config: {target_json}\ndisabled_rules:\n - force_unwrapping\n - missing_docs\n - no_magic_numbers\n" }
|
|
15
17
|
target = ".gspot/config/swiftlint.yml"
|
|
16
18
|
tool = "swiftlint"
|
|
17
|
-
per_scope = true
|
|
18
|
-
fragment = true
|
|
19
|
-
pointer = { path = ".swiftlint.yml", directories = ["**/*Tests"], body = "parent_config: {target_json}\ndisabled_rules:\n - force_unwrapping\n - missing_docs\n - no_magic_numbers\n" }
|
|
20
19
|
|
|
21
20
|
[[check]]
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
example = "`throw XCTSkip()` in a Swift test reports a missing reason. Restore the test, or give a reason such as `throw XCTSkip(\"Requires the hardware test device\")`."
|
|
22
|
+
files = { tags = ["swift-test"] }
|
|
23
|
+
help = "Write why the test stays off and what turns it on again, or delete it."
|
|
24
24
|
level = "recommended"
|
|
25
|
+
name = "skip-reasons"
|
|
25
26
|
stage = "commit"
|
|
26
|
-
files = { tags = ["swift-test"] }
|
|
27
27
|
summary = "Checks that skip calls and disabled traits name a nonempty reason, and unavailable tests carry an explanation."
|
|
28
|
-
why = "A test
|
|
29
|
-
help = "Say why the test is off and what turns it back on, or delete the test."
|
|
30
|
-
|
|
31
|
-
[[check]]
|
|
32
|
-
name = "sleep"
|
|
33
|
-
example = "A test using a fixed sleep to wait for asynchronous work reports a timing finding. Wait for the actual completion expectation or poll the required condition with a bounded timeout."
|
|
34
|
-
level = "all"
|
|
35
|
-
stage = "commit"
|
|
36
|
-
files = { tags = ["swift-test"] }
|
|
37
|
-
summary = "Refuses sleep calls in test files."
|
|
38
|
-
why = "A sleep makes a test slow when it passes and flaky when the machine is slower than the number."
|
|
39
|
-
help = "Wait on an expectation or a confirmation, or poll a condition with a timeout."
|
|
28
|
+
why = "A skipped test with no reason stays off, because nobody can tell when to restore it."
|
|
40
29
|
|
|
41
30
|
[[check]]
|
|
42
|
-
name = "coverage"
|
|
43
31
|
example = "A Swift target reports 85% line coverage against a 90% floor. Add a test that exercises its uncovered lines, then rerun the target."
|
|
32
|
+
files = { extensions = [".swift"] }
|
|
33
|
+
help = "Add tests for the target, or lower its floor with a reason."
|
|
44
34
|
level = "recommended"
|
|
45
|
-
|
|
46
|
-
runs = "scope"
|
|
35
|
+
name = "coverage"
|
|
47
36
|
needs = ["build"]
|
|
48
37
|
operating_systems = ["macos", "linux"]
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
runs = "scope"
|
|
39
|
+
stage = "push"
|
|
40
|
+
summary = "Measures each source target in Xcode projects and Swift packages against `coverage.lines` and its `coverage.swift_target` floor."
|
|
41
|
+
why = "A floor for each target exposes Swift code that no test runs."
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Swift Tests
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Read this rule when you write or change a Swift test.
|
|
4
|
+
|
|
5
|
+
These rules cover XCTest, Swift Testing, and snapshot tests.
|
|
4
6
|
|
|
5
7
|
## What to test
|
|
6
8
|
|
|
@@ -11,7 +13,7 @@ When you write tests, these rules cover XCTest, Swift Testing, and snapshot test
|
|
|
11
13
|
- When the project uses view models, assert state transitions and what the application shows to the user, never private method calls.
|
|
12
14
|
- Test invalid payload mapping and validate external data when it enters the program.
|
|
13
15
|
- Test cancellation and stale results for asynchronous operations.
|
|
14
|
-
- Do not mock SwiftUI
|
|
16
|
+
- Do not mock SwiftUI.
|
|
15
17
|
- Do not test trivial getters, setters, or framework behavior.
|
|
16
18
|
- Do not add a protocol only to make a mock when the code already accepts the network or database client as an argument.
|
|
17
19
|
|
|
@@ -31,7 +33,7 @@ When you write tests, these rules cover XCTest, Swift Testing, and snapshot test
|
|
|
31
33
|
|
|
32
34
|
- Test data shows the behavior under test.
|
|
33
35
|
- Prefer private builders or stubs with sensible defaults when model construction hides the point of the test.
|
|
34
|
-
- Keep
|
|
36
|
+
- Keep builders beside the tests that use them, unless many test files share them on purpose.
|
|
35
37
|
- Decode JSON test data only when the test covers decoding, mapping, or the format of an external payload.
|
|
36
38
|
- Do not add a production default to make a test shorter.
|
|
37
39
|
- Prefer stable identifiers, dates, clocks, and ordering.
|
|
@@ -48,7 +50,7 @@ private extension Message {
|
|
|
48
50
|
}
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
A test setup API never becomes a second model layer.
|
|
52
54
|
|
|
53
55
|
## Style
|
|
54
56
|
|
|
@@ -57,7 +59,7 @@ Test builders belong in test code. A test setup API never becomes a second model
|
|
|
57
59
|
- In XCTest, prefer throwing test methods with `try XCTUnwrap`.
|
|
58
60
|
- In Swift Testing, prefer `try #require`.
|
|
59
61
|
- Avoid `guard` in a test when an assertion helper states the failure directly.
|
|
60
|
-
- Make
|
|
62
|
+
- Make helpers and test data private unless several tests need them.
|
|
61
63
|
|
|
62
64
|
## Disabled tests
|
|
63
65
|
|