@@ -49,6 +49,56 @@ describe('health gating', function () {
4949 assert . ok ( res . out . includes ( 'HEALTH-GATE FAIL' ) ) ;
5050 } ) ;
5151
52+ it ( 'passes gate when score equals threshold (>= semantics)' , function ( ) {
53+ const cwd = mkTmp ( ) ;
54+ // exec=1000, structural=3 → overall=70
55+ const cats = { magicNumbers : [ ] , complexity : Array . from ( { length :3 } , ( ) => ( { } ) ) , domainTerms : [ ] , architecture : [ ] , counts : { } } ;
56+ const base = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
57+ const curr = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
58+ const cfg = { ratchet : { health : { enabled : true , gateOn : 'overall' , minOverall : 70 } } } ;
59+ const res = runRatchet ( cwd , base , curr , { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfg ) } ) ;
60+ assert . strictEqual ( res . code , 0 ) ;
61+ } ) ;
62+
63+ it ( 'respects gateOn: passes with structural gate, fails with semantic gate' , function ( ) {
64+ const cwd = mkTmp ( ) ;
65+ // structural=1 (score=90), semantic=5 (score=50), overall=6 (score=40)
66+ const cats = { magicNumbers : Array . from ( { length :5 } , ( ) => ( { } ) ) , complexity : Array . from ( { length :1 } , ( ) => ( { } ) ) , domainTerms : [ ] , architecture : [ ] , counts : { } } ;
67+ const base = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
68+ const curr = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
69+ const cfgStruct = { ratchet : { health : { enabled : true , gateOn : 'structural' , minOverall : 90 } } } ;
70+ const cfgSem = { ratchet : { health : { enabled : true , gateOn : 'semantic' , minOverall : 90 } } } ;
71+ const resStruct = runRatchet ( cwd , base , curr , { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfgStruct ) } ) ;
72+ const resSem = runRatchet ( cwd , base , curr , { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfgSem ) } ) ;
73+ assert . strictEqual ( resStruct . code , 0 ) ;
74+ assert . ok ( resSem . out . includes ( 'HEALTH-GATE FAIL' ) ) ;
75+ } ) ;
76+
77+ it ( 'applies intentOverrides when intent=refactoring' , function ( ) {
78+ const cwd = mkTmp ( ) ;
79+ const cats = { magicNumbers : [ ] , complexity : Array . from ( { length :3 } , ( ) => ( { } ) ) , domainTerms : [ ] , architecture : [ ] , counts : { } } ; // overall=70
80+ const base = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
81+ const curr = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
82+ const cfg = { ratchet : { health : { enabled : true , gateOn : 'overall' , minOverall : 80 , intentOverrides : { refactoring : { minOverall : 60 } } } } } ;
83+ // Without intent override → fail
84+ const res1 = runRatchet ( cwd , base , curr , { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfg ) } ) ;
85+ assert . ok ( res1 . out . includes ( 'HEALTH-GATE FAIL' ) ) ;
86+ // With intent=refactoring → pass
87+ const env2 = { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfg ) } ;
88+ // pass extra arg via environment by setting AI_SNIFFTEST_ARGS? Not supported; append to argv instead
89+ // We call node directly including --intent=refactoring by modifying runRatchet here
90+ const b = path . join ( cwd , 'baseline.json' ) ;
91+ const c = path . join ( cwd , 'current.json' ) ;
92+ writeJson ( b , base ) ;
93+ writeJson ( c , curr ) ;
94+ try {
95+ const out = cp . execFileSync ( process . execPath , [ path . join ( process . cwd ( ) , 'scripts/ratchet.js' ) , `--baseline=${ b } ` , `--current=${ c } ` , '--intent=refactoring' ] , { cwd, env : { ...process . env , ...env2 } , stdio : 'pipe' } ) ;
96+ assert . strictEqual ( String ( out ) . includes ( 'HEALTH-GATE FAIL' ) , false ) ;
97+ } catch ( e ) {
98+ assert . fail ( 'expected pass with intent override' ) ;
99+ }
100+ } ) ;
101+
52102 it ( 'bypasses gate when HEALTH_BYPASS=true' , function ( ) {
53103 const cwd = mkTmp ( ) ;
54104 // Make baseline and current have same category counts so deltas=0 (avoid traditional ratchet failure)
@@ -60,4 +110,20 @@ describe('health gating', function () {
60110 // Bypass active: gating not enforced; exit should be success since there are no deltas
61111 assert . strictEqual ( res . code , 0 ) ;
62112 } ) ;
113+
114+ it ( 'bypasses gate via commit token in latest commit message' , function ( ) {
115+ const cwd = mkTmp ( ) ;
116+ // Keep deltas=0
117+ const cats = { magicNumbers : Array . from ( { length :20 } , ( ) => ( { } ) ) , complexity : [ ] , domainTerms : [ ] , architecture : [ ] , counts : { } } ; // semantic heavy → low score
118+ const base = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
119+ const curr = { categories : cats , effort : { byCategory : { } } , lines : { physical : 1000 , executable : 1000 } , meta : { } } ;
120+ // Initialize a git repo and commit with bypass token
121+ cp . execSync ( 'git init' , { cwd } ) ;
122+ fs . writeFileSync ( path . join ( cwd , 'tmp.txt' ) , 'x' ) ;
123+ cp . execSync ( 'git add .' , { cwd } ) ;
124+ cp . execSync ( "git -c user.email=test@example.com -c user.name='Test' commit -m '[health-bypass] demo'" , { cwd } ) ;
125+ const cfg = { ratchet : { health : { enabled : true , gateOn : 'semantic' , minOverall : 90 , bypass : { commitToken : '[health-bypass]' } } } } ;
126+ const res = runRatchet ( cwd , base , curr , { AI_SNIFFTEST_CONFIG_JSON : JSON . stringify ( cfg ) } ) ;
127+ assert . strictEqual ( res . code , 0 ) ;
128+ } ) ;
63129} ) ;
0 commit comments