@@ -17,8 +17,13 @@ import { SeaNativeLoader, SeaNativeBinding } from '../../../lib/sea/SeaNativeLoa
1717
1818// Pure-logic tests for SeaNativeLoader. These exercise the load-failure
1919// hint branches, the Node-version gate, the shape check, and caching via
20- // the injectable `load` seam — so they run everywhere regardless of
21- // whether a real `.node` is installed on the test machine.
20+ // the injectable `load` and `nodeMajor` seams — so they run everywhere
21+ // regardless of whether a real `.node` is installed on the test machine
22+ // OR which Node version the runner happens to be (the CI matrix spans
23+ // 14–20, below and above the >=18 floor). Tests that exercise the load
24+ // path inject a supported Node major so the version gate never short-
25+ // circuits them; the gate's own tests inject the version under test.
26+ const SUPPORTED_NODE_MAJOR = ( ) => 18 ;
2227
2328function stubBinding ( overrides : Partial < Record < keyof SeaNativeBinding , unknown > > = { } ) : SeaNativeBinding {
2429 return {
@@ -52,7 +57,7 @@ describe('SeaNativeLoader', () => {
5257 describe ( 'successful load' , ( ) => {
5358 it ( 'get() returns the binding from the injected loader' , ( ) => {
5459 const binding = stubBinding ( ) ;
55- const loader = new SeaNativeLoader ( ( ) => binding ) ;
60+ const loader = new SeaNativeLoader ( ( ) => binding , SUPPORTED_NODE_MAJOR ) ;
5661 expect ( loader . get ( ) ) . to . equal ( binding ) ;
5762 expect ( loader . tryGet ( ) ) . to . equal ( binding ) ;
5863 } ) ;
@@ -63,7 +68,7 @@ describe('SeaNativeLoader', () => {
6368 const loader = new SeaNativeLoader ( ( ) => {
6469 calls += 1 ;
6570 return binding ;
66- } ) ;
71+ } , SUPPORTED_NODE_MAJOR ) ;
6772 loader . get ( ) ;
6873 loader . tryGet ( ) ;
6974 loader . get ( ) ;
@@ -75,7 +80,7 @@ describe('SeaNativeLoader', () => {
7580 it ( 'MODULE_NOT_FOUND → "not installed" hint pointing at the README' , ( ) => {
7681 const loader = new SeaNativeLoader ( ( ) => {
7782 throw errWithCode ( 'MODULE_NOT_FOUND' , "Cannot find module '../../native/sea'" ) ;
78- } ) ;
83+ } , SUPPORTED_NODE_MAJOR ) ;
7984 expect ( loader . tryGet ( ) ) . to . equal ( undefined ) ;
8085 const msg = thrownMessage ( ( ) => loader . get ( ) ) ;
8186 expect ( msg ) . to . match ( / n o t i n s t a l l e d / ) ;
@@ -85,7 +90,7 @@ describe('SeaNativeLoader', () => {
8590 it ( 'ERR_DLOPEN_FAILED → includes the underlying dlerror string and remediation' , ( ) => {
8691 const loader = new SeaNativeLoader ( ( ) => {
8792 throw errWithCode ( 'ERR_DLOPEN_FAILED' , 'GLIBC_2.32 not found' ) ;
88- } ) ;
93+ } , SUPPORTED_NODE_MAJOR ) ;
8994 const msg = thrownMessage ( ( ) => loader . get ( ) ) ;
9095 expect ( msg ) . to . match ( / G L I B C _ 2 \. 3 2 n o t f o u n d / ) ;
9196 expect ( msg ) . to . match ( / m u s l / ) ;
@@ -95,22 +100,22 @@ describe('SeaNativeLoader', () => {
95100 it ( 'a generic Error (no code) preserves its message' , ( ) => {
96101 const loader = new SeaNativeLoader ( ( ) => {
97102 throw new Error ( 'totally unexpected' ) ;
98- } ) ;
103+ } , SUPPORTED_NODE_MAJOR ) ;
99104 expect ( ( ) => loader . get ( ) ) . to . throw ( / t o t a l l y u n e x p e c t e d / ) ;
100105 } ) ;
101106
102107 it ( 'a non-Error throw is wrapped' , ( ) => {
103108 const loader = new SeaNativeLoader ( ( ) => {
104109 // eslint-disable-next-line no-throw-literal
105110 throw 'a string' ;
106- } ) ;
111+ } , SUPPORTED_NODE_MAJOR ) ;
107112 expect ( ( ) => loader . get ( ) ) . to . throw ( / n o n - s t a n d a r d e r r o r / ) ;
108113 } ) ;
109114 } ) ;
110115
111116 describe ( 'shape check' , ( ) => {
112117 it ( 'rejects a binding missing an expected export' , ( ) => {
113- const loader = new SeaNativeLoader ( ( ) => stubBinding ( { openSession : undefined } ) ) ;
118+ const loader = new SeaNativeLoader ( ( ) => stubBinding ( { openSession : undefined } ) , SUPPORTED_NODE_MAJOR ) ;
114119 expect ( loader . tryGet ( ) ) . to . equal ( undefined ) ;
115120 const msg = thrownMessage ( ( ) => loader . get ( ) ) ;
116121 expect ( msg ) . to . match ( / m i s s i n g e x p e c t e d e x p o r t / ) ;
@@ -120,30 +125,18 @@ describe('SeaNativeLoader', () => {
120125
121126 describe ( 'Node-version gate' , ( ) => {
122127 it ( 'fails closed on a Node version below the floor' , ( ) => {
123- const original = process . version ;
124- try {
125- Object . defineProperty ( process , 'version' , { value : 'v16.20.0' , configurable : true } ) ;
126- let loadCalled = false ;
127- const loader = new SeaNativeLoader ( ( ) => {
128- loadCalled = true ;
129- return stubBinding ( ) ;
130- } ) ;
131- expect ( ( ) => loader . get ( ) ) . to . throw ( / r e q u i r e s N o d e > = 1 8 / ) ;
132- expect ( loadCalled , 'load() must not be attempted on an unsupported Node' ) . to . equal ( false ) ;
133- } finally {
134- Object . defineProperty ( process , 'version' , { value : original , configurable : true } ) ;
135- }
128+ let loadCalled = false ;
129+ const loader = new SeaNativeLoader ( ( ) => {
130+ loadCalled = true ;
131+ return stubBinding ( ) ;
132+ } , ( ) => 16 ) ;
133+ expect ( ( ) => loader . get ( ) ) . to . throw ( / r e q u i r e s N o d e > = 1 8 / ) ;
134+ expect ( loadCalled , 'load() must not be attempted on an unsupported Node' ) . to . equal ( false ) ;
136135 } ) ;
137136
138137 it ( 'fails closed when the Node version is unparseable (NaN)' , ( ) => {
139- const original = process . version ;
140- try {
141- Object . defineProperty ( process , 'version' , { value : 'vNOT-A-VERSION' , configurable : true } ) ;
142- const loader = new SeaNativeLoader ( ( ) => stubBinding ( ) ) ;
143- expect ( ( ) => loader . get ( ) ) . to . throw ( / r e q u i r e s N o d e > = 1 8 / ) ;
144- } finally {
145- Object . defineProperty ( process , 'version' , { value : original , configurable : true } ) ;
146- }
138+ const loader = new SeaNativeLoader ( ( ) => stubBinding ( ) , ( ) => NaN ) ;
139+ expect ( ( ) => loader . get ( ) ) . to . throw ( / r e q u i r e s N o d e > = 1 8 / ) ;
147140 } ) ;
148141 } ) ;
149142} ) ;
0 commit comments