@@ -12,8 +12,8 @@ import {
1212} from "./artifacts" ;
1313import { REPO_ROOT , STATE_DIR , TEST_GREP , composePath , resolveServiceOverrides } from "./layout" ;
1414import { STEP_NAMES } from "./types" ;
15- import { main , overrideWarnings , probeBootstrap , resolveUpgradePlan } from "./runtime" ;
16- import { compatPolicyForState , requiresMultichainAclAddress } from "./compat" ;
15+ import { main , overrideWarnings , postBootHealthGate , probeBootstrap , resolveUpgradePlan } from "./runtime" ;
16+ import { compatPolicyForState , requiresMultichainAclAddress , validateBundleCompatibility } from "./compat" ;
1717import { predictedCrsId , predictedKeyId } from "./utils" ;
1818import { applyVersionEnvOverrides , createGitHubClient , resolveTarget } from "./versions" ;
1919import {
@@ -1353,3 +1353,84 @@ describe("version resolution edge cases", () => {
13531353 expect ( result ) . toBe ( original ) ;
13541354 } ) ;
13551355} ) ;
1356+
1357+ describe ( "validateBundleCompatibility" , ( ) => {
1358+ const stateWithVersions = ( relayer : string , testSuite : string ) =>
1359+ stubState ( { envOverrides : { RELAYER_VERSION : relayer , TEST_SUITE_VERSION : testSuite } } ) ;
1360+
1361+ test ( "detects relayer v1 vs test-suite v2 mismatch" , ( ) => {
1362+ const issues = validateBundleCompatibility ( stateWithVersions ( "v0.9.0" , "v0.11.0" ) ) ;
1363+ expect ( issues ) . toHaveLength ( 1 ) ;
1364+ expect ( issues [ 0 ] . code ) . toBe ( "relayer-v1-vs-test-suite-v2" ) ;
1365+ } ) ;
1366+
1367+ test ( "modern relayer is OK" , ( ) => {
1368+ expect ( validateBundleCompatibility ( stateWithVersions ( "v0.10.0" , "v0.11.0" ) ) ) . toEqual ( [ ] ) ;
1369+ } ) ;
1370+
1371+ test ( "legacy test-suite is OK" , ( ) => {
1372+ expect ( validateBundleCompatibility ( stateWithVersions ( "v0.9.0" , "v0.10.0" ) ) ) . toEqual ( [ ] ) ;
1373+ } ) ;
1374+
1375+ test ( "both modern is OK" , ( ) => {
1376+ expect ( validateBundleCompatibility ( stateWithVersions ( "v0.10.0" , "v0.12.0" ) ) ) . toEqual ( [ ] ) ;
1377+ } ) ;
1378+
1379+ test ( "SHA relayer treated as modern" , ( ) => {
1380+ expect ( validateBundleCompatibility ( stateWithVersions ( "abc1234" , "v0.11.0" ) ) ) . toEqual ( [ ] ) ;
1381+ } ) ;
1382+
1383+ test ( "SHA test-suite treated as modern triggers mismatch" , ( ) => {
1384+ const issues = validateBundleCompatibility ( stateWithVersions ( "v0.9.0" , "abc1234" ) ) ;
1385+ expect ( issues ) . toHaveLength ( 1 ) ;
1386+ expect ( issues [ 0 ] . code ) . toBe ( "relayer-v1-vs-test-suite-v2" ) ;
1387+ } ) ;
1388+
1389+ test ( "empty versions treated as modern" , ( ) => {
1390+ expect ( validateBundleCompatibility ( stateWithVersions ( "" , "" ) ) ) . toEqual ( [ ] ) ;
1391+ } ) ;
1392+
1393+ test ( "boundary v0.10.0 relayer is OK" , ( ) => {
1394+ expect ( validateBundleCompatibility ( stateWithVersions ( "v0.10.0" , "v0.11.0" ) ) ) . toEqual ( [ ] ) ;
1395+ } ) ;
1396+ } ) ;
1397+
1398+ describe ( "postBootHealthGate" , ( ) => {
1399+ const inspectResult = ( status : string , exitCode : number ) =>
1400+ JSON . stringify ( [ { Name : "test" , State : { Status : status , ExitCode : exitCode } , NetworkSettings : { Networks : { } } } ] ) ;
1401+
1402+ test ( "resolves when all containers are running" , async ( ) => {
1403+ const runner = fakeRunner ( {
1404+ "docker inspect container-a" : inspectResult ( "running" , 0 ) ,
1405+ "docker inspect container-b" : inspectResult ( "running" , 0 ) ,
1406+ } ) ;
1407+ await postBootHealthGate ( { runner } , [ "container-a" , "container-b" ] , 0 ) ;
1408+ } ) ;
1409+
1410+ test ( "throws when a container crashed" , async ( ) => {
1411+ const runner = fakeRunner ( {
1412+ "docker inspect container-a" : inspectResult ( "running" , 0 ) ,
1413+ "docker inspect container-b" : inspectResult ( "exited" , 1 ) ,
1414+ "docker logs --tail 30 container-b" : "Error: missing API key" ,
1415+ } ) ;
1416+ await expect (
1417+ postBootHealthGate ( { runner } , [ "container-a" , "container-b" ] , 0 ) ,
1418+ ) . rejects . toThrow ( / c o n t a i n e r - b .* e x i t 1 / ) ;
1419+ } ) ;
1420+
1421+ test ( "throws when container not found" , async ( ) => {
1422+ const runner = fakeRunner ( {
1423+ "docker inspect container-a" : { stdout : "" , stderr : "" , code : 1 } ,
1424+ } ) ;
1425+ await expect (
1426+ postBootHealthGate ( { runner } , [ "container-a" ] , 0 ) ,
1427+ ) . rejects . toThrow ( / c o n t a i n e r n o t f o u n d / ) ;
1428+ } ) ;
1429+
1430+ test ( "ignores containers that exited with code 0 (migrations)" , async ( ) => {
1431+ const runner = fakeRunner ( {
1432+ "docker inspect container-a" : inspectResult ( "exited" , 0 ) ,
1433+ } ) ;
1434+ await postBootHealthGate ( { runner } , [ "container-a" ] , 0 ) ;
1435+ } ) ;
1436+ } ) ;
0 commit comments