tamperward 2.24.2 → 2.24.3

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 (2) hide show
  1. package/dist/cli/index.js +109 -13
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -3713,6 +3713,83 @@ var init_lint_suppression = __esm({
3713
3713
  }
3714
3714
  });
3715
3715
 
3716
+ // src/detectors/runner-chain.ts
3717
+ function readRunnerChain(root, props) {
3718
+ let runner = root;
3719
+ const modifiers = [];
3720
+ let table = null;
3721
+ let skipMarker = null;
3722
+ let skipIndex = -1;
3723
+ for (let i = 0; i < props.length; i++) {
3724
+ const p = props[i];
3725
+ if (table) return null;
3726
+ if (RUNNER_NAMES.has(p) && modifiers.length === 0) {
3727
+ runner = p;
3728
+ continue;
3729
+ }
3730
+ if (CHAIN_MODIFIERS.has(p)) {
3731
+ modifiers.push(p);
3732
+ const marker = skipMarkerOf(p);
3733
+ if (marker && !skipMarker) {
3734
+ skipMarker = marker;
3735
+ skipIndex = i;
3736
+ }
3737
+ continue;
3738
+ }
3739
+ const t = tableOf(p);
3740
+ if (t) {
3741
+ table = t;
3742
+ continue;
3743
+ }
3744
+ return null;
3745
+ }
3746
+ return { runner, modifiers, table, skipMarker, skipIndex };
3747
+ }
3748
+ function readCallee(expr) {
3749
+ const props = [];
3750
+ let cur = expr;
3751
+ while (ts.isPropertyAccessExpression(cur) || ts.isElementAccessExpression(cur)) {
3752
+ if (ts.isPropertyAccessExpression(cur)) {
3753
+ props.unshift(cur.name.text);
3754
+ } else {
3755
+ const arg = cur.argumentExpression;
3756
+ if (!ts.isStringLiteralLike(arg)) return null;
3757
+ props.unshift(arg.text);
3758
+ }
3759
+ cur = cur.expression;
3760
+ }
3761
+ if (!ts.isIdentifier(cur)) return null;
3762
+ return readRunnerChain(cur.text, props);
3763
+ }
3764
+ function readTableCallee(expr) {
3765
+ const head = ts.isCallExpression(expr) ? expr.expression : ts.isTaggedTemplateExpression(expr) ? expr.tag : null;
3766
+ if (!head) return null;
3767
+ const chain = readCallee(head);
3768
+ return chain?.table ? chain : null;
3769
+ }
3770
+ var RUNNER_NAMES, CHAIN_MODIFIERS, skipMarkerOf, tableOf;
3771
+ var init_runner_chain = __esm({
3772
+ "src/detectors/runner-chain.ts"() {
3773
+ "use strict";
3774
+ init_ts_lazy();
3775
+ RUNNER_NAMES = /* @__PURE__ */ new Set(["it", "test", "describe", "suite"]);
3776
+ CHAIN_MODIFIERS = /* @__PURE__ */ new Set([
3777
+ "concurrent",
3778
+ "sequential",
3779
+ "shuffle",
3780
+ "serial",
3781
+ "parallel",
3782
+ "skip",
3783
+ "only",
3784
+ "todo",
3785
+ "fails",
3786
+ "failing"
3787
+ ]);
3788
+ skipMarkerOf = (name) => name === "skip" || name === "only" || name === "todo" ? name : null;
3789
+ tableOf = (name) => name === "each" || name === "for" ? name : null;
3790
+ }
3791
+ });
3792
+
3716
3793
  // src/detectors/test-skip.ts
3717
3794
  import { posix as posix2 } from "node:path";
3718
3795
  function pyMarkAliases(source) {
@@ -3886,12 +3963,14 @@ function staticPropertyName(expr, ctx, strings) {
3886
3963
  }
3887
3964
  function runnerChain(expr, ctx, binder, strings) {
3888
3965
  const props = [];
3966
+ const propNodes = [];
3889
3967
  const causes = [];
3890
3968
  let cur = expr;
3891
3969
  let terminalNode = expr;
3892
3970
  while (ts.isPropertyAccessExpression(cur) || ts.isElementAccessExpression(cur)) {
3893
3971
  if (ts.isPropertyAccessExpression(cur)) {
3894
3972
  props.unshift(cur.name.text);
3973
+ propNodes.unshift(cur.name);
3895
3974
  if (props.length === 1) terminalNode = cur.name;
3896
3975
  cur = cur.expression;
3897
3976
  continue;
@@ -3900,6 +3979,7 @@ function runnerChain(expr, ctx, binder, strings) {
3900
3979
  const property = staticPropertyName(cur.argumentExpression, ctx, strings);
3901
3980
  if (!property) return null;
3902
3981
  props.unshift(property.name);
3982
+ propNodes.unshift(cur.argumentExpression);
3903
3983
  causes.push(...property.causes);
3904
3984
  if (props.length === 1) terminalNode = cur.argumentExpression;
3905
3985
  cur = cur.expression;
@@ -3916,17 +3996,21 @@ function runnerChain(expr, ctx, binder, strings) {
3916
3996
  }
3917
3997
  } else {
3918
3998
  namespace = requiredModule(cur);
3999
+ if (namespace == null && readTableCallee(cur)) return null;
3919
4000
  verdict2 = namespace == null ? UNKNOWN : NON_RUNNER;
3920
4001
  }
3921
4002
  let root = ts.isIdentifier(cur) ? cur.text : "";
3922
4003
  if (namespace != null && props.length > 0) {
3923
4004
  verdict2 = withCause(binder.memberOf(namespace, props[0]), cur);
3924
- if (verdict2.kind === "runner") root = props.shift() ?? root;
4005
+ if (verdict2.kind === "runner") {
4006
+ root = props.shift() ?? root;
4007
+ propNodes.shift();
4008
+ }
3925
4009
  }
3926
4010
  if (verdict2.kind === "unknown") return "unknown";
3927
4011
  if (verdict2.kind === "non-runner") return null;
3928
4012
  causes.push(...verdict2.causes);
3929
- return { root, props, terminalNode, causes };
4013
+ return { root, props, propNodes, terminalNode, causes };
3930
4014
  }
3931
4015
  function optionDisables(value) {
3932
4016
  return !(value.kind === ts.SyntaxKind.FalseKeyword || ts.isNumericLiteral(value) && Number(value.text) === 0);
@@ -3965,8 +4049,9 @@ function semanticSkipHits(ctx, binder) {
3965
4049
  const last = lineOf(callee.getEnd());
3966
4050
  for (let line = first; line <= last; line++) unowned.add(line);
3967
4051
  } else if (chain) {
3968
- const { props, terminalNode, causes } = chain;
4052
+ const { root, props, propNodes, terminalNode, causes } = chain;
3969
4053
  const terminal = props.at(-1);
4054
+ const shape = readRunnerChain(root, props);
3970
4055
  const push = (target, extraCauses, why) => {
3971
4056
  hits.push({
3972
4057
  semanticKey: `${callKey}\0${why}`,
@@ -3976,13 +4061,13 @@ function semanticSkipHits(ctx, binder) {
3976
4061
  evidence: callText
3977
4062
  });
3978
4063
  };
3979
- if (terminal && (terminal === "skip" || terminal === "only" || terminal === "todo") && props.slice(0, -1).every((p) => JS_CHAIN_MODIFIERS.has(p) || JS_RUNNERS.has(p))) {
3980
- push(terminalNode, [], "a .skip/.only/.todo marker");
4064
+ if (shape?.skipMarker) {
4065
+ push(propNodes[shape.skipIndex] ?? terminalNode, [], "a .skip/.only/.todo marker");
3981
4066
  } else if (terminal && ["skipIf", "runIf"].includes(terminal) && props.length === 1) {
3982
4067
  push(terminalNode, [], "a .skipIf()/.runIf() condition (the test runs only when the condition allows)");
3983
4068
  } else if (terminal && ["fails", "failing"].includes(terminal) && props.length === 1) {
3984
4069
  push(terminalNode, [], "a .fails/.failing marker (the test now passes by failing)");
3985
- } else if (terminal === "each" && props.length === 1 && node.arguments.length > 0 && ts.isArrayLiteralExpression(node.arguments[0]) && node.arguments[0].elements.length === 0) {
4070
+ } else if (shape?.table === "each" && node.arguments.length > 0 && ts.isArrayLiteralExpression(node.arguments[0]) && node.arguments[0].elements.length === 0) {
3986
4071
  push(terminalNode, [], "an empty .each table (no case ever runs)");
3987
4072
  }
3988
4073
  if (props.length === 0) {
@@ -4062,11 +4147,12 @@ function astSkipHits(c, resolvers) {
4062
4147
  }
4063
4148
  return { hits, authoritative: true, unowned: analysis.unowned };
4064
4149
  }
4065
- var RULE4, acc, JS_RUNNER, JS_MOD, GO_SKIP_PATTERNS, PY_MARK, PY_CONFTEST_PATTERNS, PY_MARK_ALIAS, PY_DECORATOR, PATTERNS2, JS_RUNNERS, JS_CHAIN_MODIFIERS, JS_DEFAULT_RUNNER_MODULES, JS_TEST_MODULES, RUNNER2, NON_RUNNER, UNKNOWN, withCause, MODULE_EXTENSIONS, RunnerResolver, ModuleBinder, NO_AST, testSkip;
4150
+ var RULE4, acc, JS_RUNNER, JS_MOD, GO_SKIP_PATTERNS, PY_MARK, PY_CONFTEST_PATTERNS, PY_MARK_ALIAS, PY_DECORATOR, PATTERNS2, JS_RUNNERS, JS_DEFAULT_RUNNER_MODULES, JS_TEST_MODULES, RUNNER2, NON_RUNNER, UNKNOWN, withCause, MODULE_EXTENSIONS, RunnerResolver, ModuleBinder, NO_AST, testSkip;
4066
4151
  var init_test_skip = __esm({
4067
4152
  "src/detectors/test-skip.ts"() {
4068
4153
  "use strict";
4069
4154
  init_ts_lazy();
4155
+ init_runner_chain();
4070
4156
  init_select();
4071
4157
  init_policy();
4072
4158
  init_repo();
@@ -4172,7 +4258,6 @@ var init_test_skip = __esm({
4172
4258
  ]
4173
4259
  };
4174
4260
  JS_RUNNERS = /* @__PURE__ */ new Set(["it", "test", "describe", "suite"]);
4175
- JS_CHAIN_MODIFIERS = /* @__PURE__ */ new Set(["concurrent", "sequential", "shuffle", "serial", "parallel"]);
4176
4261
  JS_DEFAULT_RUNNER_MODULES = /* @__PURE__ */ new Set(["node:test", "node:test/promises", "@playwright/test"]);
4177
4262
  JS_TEST_MODULES = /* @__PURE__ */ new Set([
4178
4263
  "vitest",
@@ -7878,6 +7963,8 @@ function calleeName(expr) {
7878
7963
  if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression)) {
7879
7964
  return expr.expression.text;
7880
7965
  }
7966
+ const chain = readCallee(expr);
7967
+ if (chain && !chain.table) return chain.runner;
7881
7968
  return null;
7882
7969
  }
7883
7970
  function loopRows(iterable) {
@@ -8098,7 +8185,7 @@ function pytestPrecedenceFindings(changes, policy, ctx) {
8098
8185
  })
8099
8186
  );
8100
8187
  }
8101
- var RULE9, JVM_TEST, TEST_DEFS, TABLE_METHOD, isEachOf, isDescribeEach, OTHER_RUNNER_DIR, WHOLE_SUITE_SCRIPT, SLICE_SCRIPT, SED_INPLACE, testDeletion;
8188
+ var RULE9, JVM_TEST, TEST_DEFS, isTableOf, isEachOf, isDescribeEach, OTHER_RUNNER_DIR, WHOLE_SUITE_SCRIPT, SLICE_SCRIPT, SED_INPLACE, testDeletion;
8102
8189
  var init_test_deletion = __esm({
8103
8190
  "src/detectors/test-deletion.ts"() {
8104
8191
  "use strict";
@@ -8111,6 +8198,7 @@ var init_test_deletion = __esm({
8111
8198
  init_suite_config();
8112
8199
  init_invocation();
8113
8200
  init_narrow();
8201
+ init_runner_chain();
8114
8202
  RULE9 = "test-deletion";
8115
8203
  JVM_TEST = /^\s*(?:@\w+(?:\([^)]*\))?\s+)*@(?:Test|ParameterizedTest|RepeatedTest|TestFactory|TestTemplate)\b/;
8116
8204
  TEST_DEFS = {
@@ -8123,9 +8211,12 @@ var init_test_deletion = __esm({
8123
8211
  php: /^\s*(?:(?:public\s+)?function\s+test\w*\s*\(|#\[Test\]|\*\s*@test\b)/,
8124
8212
  cs: /^\s*\[(?:Fact|Theory|Test|TestMethod|TestCase|DataTestMethod)\b/
8125
8213
  };
8126
- TABLE_METHOD = /^(?:each|for)$/;
8127
- isEachOf = (expr) => ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && (expr.expression.text === "it" || expr.expression.text === "test") && TABLE_METHOD.test(expr.name.text);
8128
- isDescribeEach = (expr) => ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === "describe" && TABLE_METHOD.test(expr.name.text);
8214
+ isTableOf = (expr, runners) => {
8215
+ const chain = readCallee(expr);
8216
+ return chain != null && chain.table != null && runners.includes(chain.runner);
8217
+ };
8218
+ isEachOf = (expr) => isTableOf(expr, ["it", "test"]);
8219
+ isDescribeEach = (expr) => isTableOf(expr, ["describe"]);
8129
8220
  OTHER_RUNNER_DIR = /(?:^|\/)(?:e2e|cypress|playwright|fixtures|__fixtures__|__mocks__)\//;
8130
8221
  WHOLE_SUITE_SCRIPT = /^(?:test|test:ci|check|pretest)$/;
8131
8222
  SLICE_SCRIPT = /^test:(?:unit|integration)$/;
@@ -8635,7 +8726,11 @@ function expectAssertion(node, sf) {
8635
8726
  };
8636
8727
  }
8637
8728
  function simpleCallName(node) {
8638
- return ts.isIdentifier(node.expression) ? node.expression.text : null;
8729
+ if (ts.isIdentifier(node.expression)) return node.expression.text;
8730
+ const chain = readCallee(node.expression) ?? readTableCallee(node.expression);
8731
+ if (!chain) return null;
8732
+ if (chain.table && !ts.isCallExpression(node.expression) && !ts.isTaggedTemplateExpression(node.expression)) return null;
8733
+ return chain.runner;
8639
8734
  }
8640
8735
  function callbackFor(node) {
8641
8736
  const cb = node.arguments[1];
@@ -8755,6 +8850,7 @@ var init_assertion_weakening = __esm({
8755
8850
  init_policy();
8756
8851
  init_finding();
8757
8852
  init_files();
8853
+ init_runner_chain();
8758
8854
  RULE13 = "assertion-weakening";
8759
8855
  TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
8760
8856
  SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite", "context"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.24.2",
3
+ "version": "2.24.3",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",