testing-conventions 0.0.4__tar.gz → 0.0.6__tar.gz

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.
Files changed (51) hide show
  1. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/PKG-INFO +1 -1
  2. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/CHANGELOG.md +8 -0
  3. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/Cargo.lock +1 -1
  4. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/Cargo.toml +1 -1
  5. testing_conventions-0.0.6/rust/MIGRATIONS.md +83 -0
  6. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/src/lib.rs +19 -6
  7. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/unit_location.rs +51 -16
  8. testing_conventions-0.0.4/rust/MIGRATIONS.md +0 -61
  9. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/README.md +0 -0
  10. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/pyproject.toml +0 -0
  11. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/src/config.rs +0 -0
  12. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/src/location.rs +0 -0
  13. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/src/main.rs +0 -0
  14. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/config_loader.rs +0 -0
  15. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/malformed.toml +0 -0
  16. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/clean/pkg/helper.py +0 -0
  17. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/clean/pkg/helper_test.py +0 -0
  18. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/clean/widget.py +0 -0
  19. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/clean/widget_test.py +0 -0
  20. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/exempt/__init__.py +0 -0
  21. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/exempt/pkg/__init__.py +0 -0
  22. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/exempt/widget.py +0 -0
  23. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/exempt/widget_test.py +0 -0
  24. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/lonely.py +0 -0
  25. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/pkg/orphan.py +0 -0
  26. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/pkg/paired.py +0 -0
  27. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/pkg/paired_test.py +0 -0
  28. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/widget.py +0 -0
  29. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/red/widget_test.py +0 -0
  30. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/ambient.d.mts +0 -0
  31. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/button.test.tsx +0 -0
  32. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/button.tsx +0 -0
  33. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/legacy.cts +0 -0
  34. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/legacy.test.cts +0 -0
  35. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/pkg/helper.test.ts +0 -0
  36. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/pkg/helper.ts +0 -0
  37. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/service.mts +0 -0
  38. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/service.test.mts +0 -0
  39. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/types.d.ts +0 -0
  40. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/widget.test.ts +0 -0
  41. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/clean/widget.ts +0 -0
  42. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/bridge.cts +0 -0
  43. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/lonely.ts +0 -0
  44. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/pkg/orphan.ts +0 -0
  45. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/shapes.d.cts +0 -0
  46. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/shapes.d.ts +0 -0
  47. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/solo.mts +0 -0
  48. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/widget.test.ts +0 -0
  49. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unit_location/typescript/red/widget.ts +0 -0
  50. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/unknown_key.toml +0 -0
  51. {testing_conventions-0.0.4 → testing_conventions-0.0.6}/rust/tests/fixtures/valid.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testing-conventions
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: Enforce testing conventions in libraries (Python, TypeScript, and Rust).
5
5
  Author-email: Kevin Scott <me@thekevinscott.com>
6
6
  License: MIT
@@ -23,6 +23,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
23
23
 
24
24
  ### Changed
25
25
 
26
+ - **BREAKING** — the unit-test location check moved from the flat `unit-location`
27
+ subcommand to `unit location`: rules now nest under their test kind (`unit` is a
28
+ command group, `location` its first rule). The `--lang` flag is renamed to
29
+ `--language` and is now **required** — the `python` default is gone, so omitting
30
+ the language is a usage error instead of a silently-empty `python` scan. Migrate
31
+ `unit-location --lang typescript src/` → `unit location --language typescript src/`;
32
+ see [MIGRATIONS](./MIGRATIONS.md#unreleased). (#22)
33
+
26
34
  ### Deprecated
27
35
 
28
36
  ### Removed
@@ -226,7 +226,7 @@ dependencies = [
226
226
 
227
227
  [[package]]
228
228
  name = "testing-conventions"
229
- version = "0.0.4"
229
+ version = "0.0.6"
230
230
  dependencies = [
231
231
  "anyhow",
232
232
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "testing-conventions"
3
- version = "0.0.4"
3
+ version = "0.0.6"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  authors = ["Kevin Scott <me@thekevinscott.com>"]
@@ -0,0 +1,83 @@
1
+ # Migrations
2
+
3
+ Upgrade notes for breaking changes. New entries go under `## Unreleased`.
4
+ On release, the section is renamed to `## v<OLD> → v<NEW>`.
5
+
6
+ Each entry has five sections, in order:
7
+
8
+ 1. **Summary** — one paragraph: what changed and why.
9
+ 2. **Required changes** — before/after for public API. "None" if purely additive.
10
+ 3. **Deprecations removed** — anything previously warned about that's now gone.
11
+ 4. **Behavior changes without code changes** — same API, different runtime behavior.
12
+ 5. **Verification** — commands that confirm the upgrade worked, with expected output.
13
+
14
+ ## Unreleased
15
+
16
+ ### Summary
17
+
18
+ Adds the `config` module: a `Config` schema holding the per-language `coverage`
19
+ thresholds (`[python]` / `[typescript]` / `[rust]`), plus `load_config()`, which
20
+ reads one TOML file into it and validates the config itself (the self-guard) —
21
+ unknown keys and malformed TOML are rejected rather than silently accepted.
22
+ Purely additive; nothing consumes the parsed config yet.
23
+
24
+ Also reshapes the unit-test location/naming rule's CLI (#22). The rule itself
25
+ ships for two languages — `missing_unit_tests(root, language)` walks a directory
26
+ and returns every source file with no colocated test, and the CLI runs it and
27
+ exits non-zero on any orphan (Python #15: `foo.py` → `foo_test.py`, `__init__.py`
28
+ exempt; TypeScript #18: `foo-bar.ts` → `foo-bar.test.ts` across
29
+ `.ts`/`.tsx`/`.mts`/`.cts`, `*.d.ts`/`*.d.mts`/`*.d.cts` ignored). The
30
+ **command surface changes**, though: the previously released `unit-location
31
+ [--lang …]` (v0.0.3 / v0.0.4) becomes `unit location --language
32
+ <python|typescript> <PATH>` — rules now nest under their test kind (`unit` is a
33
+ command group, `location` its first rule) and `--language` is required (the
34
+ `python` default is gone). This is a breaking change for anyone on v0.0.4 or
35
+ earlier; the library API (`missing_unit_tests`, `Language`, `Config`,
36
+ `load_config`) is unchanged.
37
+
38
+ ### Required changes
39
+
40
+ The unit-location CLI was renamed and its language flag made required. Update any
41
+ invocation (CI steps, scripts, `npx`/`pip`/`cargo` wrappers):
42
+
43
+ | Before (≤ v0.0.4) | After |
44
+ | -------------------------------------- | ------------------------------------------ |
45
+ | `unit-location src/` | `unit location --language python src/` |
46
+ | `unit-location --lang typescript src/` | `unit location --language typescript src/` |
47
+
48
+ - `unit-location` → `unit location` (a `location` subcommand under the new `unit` group).
49
+ - `--lang` → `--language`.
50
+ - `--language` is required: there is no longer a `python` default to fall back on.
51
+
52
+ The library API is unchanged — `testing_conventions::config::{Config, load_config}`
53
+ and `testing_conventions::location::{missing_unit_tests, Language}` keep their
54
+ signatures.
55
+
56
+ ### Deprecations removed
57
+
58
+ The `--lang` flag and its implicit `python` default are gone — a clean break, not
59
+ a deprecation cycle (pre-1.0, so no prior warning was shipped).
60
+
61
+ ### Behavior changes without code changes
62
+
63
+ Omitting the language is now a usage error (exit code `2`) instead of defaulting to
64
+ `python`. Before, running the check on a TypeScript project without a flag scanned
65
+ for `*.py`, found none, and exited `0` — a silent false green; now the language
66
+ must be stated explicitly.
67
+
68
+ ### Verification
69
+
70
+ ```
71
+ cd packages/rust && cargo test --test config_loader
72
+ ```
73
+
74
+ Expected: the loader's integration tests pass — the canonical config loads into
75
+ memory, and unknown-key, malformed, and missing-file configs are rejected.
76
+
77
+ ```
78
+ cd packages/rust && cargo test --test unit_location
79
+ ```
80
+
81
+ Expected: the location check's integration tests pass for both languages — each
82
+ clean fixture reports no orphans, each red fixture reports its missing twins, and
83
+ `unit location` exits non-zero on the red fixtures.
@@ -21,13 +21,23 @@ pub struct Cli {
21
21
  enum Command {
22
22
  /// Check the repository against its testing-conventions config.
23
23
  Check,
24
+ /// Unit-test conventions.
25
+ Unit {
26
+ #[command(subcommand)]
27
+ rule: UnitRule,
28
+ },
29
+ }
30
+
31
+ /// Rules enforced on the unit-test suite (the README's "Unit" taxonomy).
32
+ #[derive(Subcommand, Debug)]
33
+ enum UnitRule {
24
34
  /// Check that every source file has a colocated unit test.
25
- UnitLocation {
35
+ Location {
26
36
  /// Directory to scan recursively.
27
37
  path: PathBuf,
28
- /// Language convention to enforce.
29
- #[arg(long, value_enum, default_value = "python")]
30
- lang: location::Language,
38
+ /// Language convention to enforce (required).
39
+ #[arg(long, value_enum)]
40
+ language: location::Language,
31
41
  },
32
42
  }
33
43
 
@@ -39,9 +49,12 @@ where
39
49
  let cli = Cli::try_parse_from(args)?;
40
50
  match cli.command {
41
51
  // The config-driven `check` umbrella isn't wired yet; the scaffold
42
- // proves the wiring while individual rules land as their own subcommand.
52
+ // proves the wiring while individual rules land under their test-kind
53
+ // group (e.g. `unit location`).
43
54
  Some(Command::Check) | None => Ok(0),
44
- Some(Command::UnitLocation { path, lang }) => run_unit_location(&path, lang),
55
+ Some(Command::Unit {
56
+ rule: UnitRule::Location { path, language },
57
+ }) => run_unit_location(&path, language),
45
58
  }
46
59
  }
47
60
 
@@ -3,8 +3,9 @@
3
3
  //!
4
4
  //! A source file must have a *colocated* test named after it: `foo.py` →
5
5
  //! `foo_test.py`, `foo-bar.ts` → `foo-bar.test.ts`. `missing_unit_tests`
6
- //! returns the source files missing their twin; the `unit-location`
7
- //! subcommand turns a non-empty result into a non-zero exit.
6
+ //! returns the source files missing their twin; the `unit location`
7
+ //! subcommand (a `location` rule nested under the `unit` kind, #22) turns a
8
+ //! non-empty result into a non-zero exit.
8
9
  //!
9
10
  //! Per the #3 guardrail, each language ships a clean fixture (every source
10
11
  //! paired — must pass) and a red fixture (orphans present — must fail).
@@ -36,14 +37,16 @@ fn relative_orphans(root: &Path, language: Language) -> Vec<String> {
36
37
  .collect()
37
38
  }
38
39
 
39
- /// Exit code of `unit-location [--lang <lang>] <fixture>`.
40
- fn unit_location_exit(fixture_name: &str, lang: Option<&str>) -> i32 {
41
- let mut argv: Vec<OsString> = vec!["testing-conventions".into(), "unit-location".into()];
42
- if let Some(lang) = lang {
43
- argv.push("--lang".into());
44
- argv.push(lang.into());
45
- }
46
- argv.push(fixture(fixture_name).into_os_string());
40
+ /// Exit code of `unit location --language <language> <fixture>`.
41
+ fn unit_location_exit(fixture_name: &str, language: &str) -> i32 {
42
+ let argv: Vec<OsString> = vec![
43
+ "testing-conventions".into(),
44
+ "unit".into(),
45
+ "location".into(),
46
+ "--language".into(),
47
+ language.into(),
48
+ fixture(fixture_name).into_os_string(),
49
+ ];
47
50
  run(argv).expect("a readable tree should not error")
48
51
  }
49
52
 
@@ -83,12 +86,12 @@ fn python_missing_root_is_an_error() {
83
86
 
84
87
  #[test]
85
88
  fn python_subcommand_exits_zero_on_a_clean_tree() {
86
- assert_eq!(unit_location_exit("clean", None), 0);
89
+ assert_eq!(unit_location_exit("clean", "python"), 0);
87
90
  }
88
91
 
89
92
  #[test]
90
93
  fn python_subcommand_exits_nonzero_on_a_red_tree() {
91
- assert_eq!(unit_location_exit("red", None), 1);
94
+ assert_eq!(unit_location_exit("red", "python"), 1);
92
95
  }
93
96
 
94
97
  // ---- TypeScript (#18) ----------------------------------------------------
@@ -113,13 +116,45 @@ fn typescript_red_tree_reports_every_missing_twin() {
113
116
 
114
117
  #[test]
115
118
  fn typescript_subcommand_exits_zero_on_a_clean_tree() {
119
+ assert_eq!(unit_location_exit("typescript/clean", "typescript"), 0);
120
+ }
121
+
122
+ #[test]
123
+ fn typescript_subcommand_exits_nonzero_on_a_red_tree() {
124
+ assert_eq!(unit_location_exit("typescript/red", "typescript"), 1);
125
+ }
126
+
127
+ // ---- CLI surface (#22) ---------------------------------------------------
128
+
129
+ /// Raw result of invoking the CLI with `args` after the program name, so a
130
+ /// usage error (clap) can be asserted rather than unwrapped away.
131
+ fn run_cli(args: &[&str]) -> anyhow::Result<i32> {
132
+ let argv: Vec<OsString> = std::iter::once(OsString::from("testing-conventions"))
133
+ .chain(args.iter().copied().map(OsString::from))
134
+ .collect();
135
+ run(argv)
136
+ }
137
+
138
+ #[test]
139
+ fn unit_location_requires_language() {
140
+ // Omitting `--language` is a usage error — never a silent `python` run.
141
+ let err = run_cli(&["unit", "location", "src"]).expect_err("--language is required");
142
+ let clap_err = err
143
+ .downcast_ref::<clap::Error>()
144
+ .expect("a missing required flag should surface as a clap::Error");
116
145
  assert_eq!(
117
- unit_location_exit("typescript/clean", Some("typescript")),
118
- 0
146
+ clap_err.kind(),
147
+ clap::error::ErrorKind::MissingRequiredArgument
119
148
  );
120
149
  }
121
150
 
122
151
  #[test]
123
- fn typescript_subcommand_exits_nonzero_on_a_red_tree() {
124
- assert_eq!(unit_location_exit("typescript/red", Some("typescript")), 1);
152
+ fn the_flat_unit_location_subcommand_is_gone() {
153
+ // The pre-#22 flat form (`unit-location …`) no longer parses; the rule now
154
+ // lives at `unit location`.
155
+ let err = run_cli(&["unit-location", "src"]).expect_err("the flat subcommand was removed");
156
+ let clap_err = err
157
+ .downcast_ref::<clap::Error>()
158
+ .expect("an unknown subcommand should surface as a clap::Error");
159
+ assert_eq!(clap_err.kind(), clap::error::ErrorKind::InvalidSubcommand);
125
160
  }
@@ -1,61 +0,0 @@
1
- # Migrations
2
-
3
- Upgrade notes for breaking changes. New entries go under `## Unreleased`.
4
- On release, the section is renamed to `## v<OLD> → v<NEW>`.
5
-
6
- Each entry has five sections, in order:
7
-
8
- 1. **Summary** — one paragraph: what changed and why.
9
- 2. **Required changes** — before/after for public API. "None" if purely additive.
10
- 3. **Deprecations removed** — anything previously warned about that's now gone.
11
- 4. **Behavior changes without code changes** — same API, different runtime behavior.
12
- 5. **Verification** — commands that confirm the upgrade worked, with expected output.
13
-
14
- ## Unreleased
15
-
16
- ### Summary
17
-
18
- Adds the `config` module: a `Config` schema holding the per-language `coverage`
19
- thresholds (`[python]` / `[typescript]` / `[rust]`), plus `load_config()`, which
20
- reads one TOML file into it and validates the config itself (the self-guard) —
21
- unknown keys and malformed TOML are rejected rather than silently accepted.
22
- Purely additive; nothing consumes the parsed config yet.
23
-
24
- Also adds the first structural rule — unit-test location/naming — for two
25
- languages. `missing_unit_tests(root, language)` walks a directory and returns
26
- every source file with no colocated test, and `unit-location [--lang …] <PATH>`
27
- runs that check and exits non-zero on any orphan. Python (#15): `foo.py` →
28
- `foo_test.py`, `__init__.py` exempt. TypeScript (#18): `foo-bar.ts` →
29
- `foo-bar.test.ts` (`.ts`/`.tsx`/`.mts`/`.cts`), `*.d.ts`/`*.d.mts`/`*.d.cts`
30
- ignored. Purely additive.
31
-
32
- ### Required changes
33
-
34
- None. New, additive API: `testing_conventions::config::{Config, load_config}`,
35
- `testing_conventions::location::{missing_unit_tests, Language}`, and the
36
- `unit-location [--lang python|typescript] <PATH>` CLI subcommand.
37
-
38
- ### Deprecations removed
39
-
40
- None.
41
-
42
- ### Behavior changes without code changes
43
-
44
- None.
45
-
46
- ### Verification
47
-
48
- ```
49
- cd packages/rust && cargo test --test config_loader
50
- ```
51
-
52
- Expected: the loader's integration tests pass — the canonical config loads into
53
- memory, and unknown-key, malformed, and missing-file configs are rejected.
54
-
55
- ```
56
- cd packages/rust && cargo test --test unit_location
57
- ```
58
-
59
- Expected: the location check's integration tests pass for both languages — each
60
- clean fixture reports no orphans, each red fixture reports its missing twins, and
61
- `unit-location` exits non-zero on the red fixtures.