@@ -711,4 +711,174 @@ describe('smoketest: object-oriented gateway config', () => {
711711 } ) ;
712712 } ,
713713 ) ;
714+
715+ // Tests for gateway config with network policy interactions
716+ // Uses a shared devbox setup similar to 'comprehensive gateway proxying tests'
717+ //
718+ // KNOWN LIMITATION: As of this writing, gateway URLs (e.g., gateway.runloop.pro)
719+ // are not accessible from devboxes that have network policies applied, even with
720+ // allow_all: true. The gateway service runs on internal infrastructure and network
721+ // policies may not properly whitelist access to it. The setup tests below verify
722+ // that devboxes can be created with both gateway configs and network policies,
723+ // and that the configuration is properly applied. The actual gateway request tests
724+ // are skipped until the backend infrastructure supports this combination.
725+ ( process . env [ 'RUN_SMOKETESTS' ] ? describe : describe . skip ) (
726+ 'gateway config with network policy' ,
727+ ( ) => {
728+ let devbox : Devbox | undefined ;
729+ let gatewayConfig : GatewayConfig | undefined ;
730+ let networkPolicy : NetworkPolicy | undefined ;
731+ const testSecretName = uniqueName ( 'gw-np-test-secret' ) ;
732+ let gatewayUrl : string ;
733+ let gatewayToken : string ;
734+
735+ beforeAll ( async ( ) => {
736+ const runloopApiKey = process . env [ 'RUNLOOP_API_KEY' ] ;
737+ expect ( runloopApiKey ) . toBeTruthy ( ) ;
738+ const baseUrl = process . env [ 'RUNLOOP_BASE_URL' ] || 'https://api.runloop.ai' ;
739+
740+ // Create network policy with allow_all for this test suite
741+ networkPolicy = await sdk . networkPolicy . create ( {
742+ name : uniqueName ( 'gw-np-test-policy' ) ,
743+ allow_all : true ,
744+ description : 'Network policy for gateway + network policy tests' ,
745+ } ) ;
746+
747+ // Create secret for gateway auth
748+ await sdk . api . secrets . create ( {
749+ name : testSecretName ,
750+ value : runloopApiKey ! ,
751+ } ) ;
752+
753+ // Create gateway config
754+ gatewayConfig = await sdk . gatewayConfig . create ( {
755+ name : uniqueName ( 'gw-np-test-gateway' ) ,
756+ endpoint : baseUrl ,
757+ auth_mechanism : { type : 'bearer' } ,
758+ } ) ;
759+
760+ // Create devbox with both gateway and network policy
761+ devbox = await sdk . devbox . create ( {
762+ name : uniqueName ( 'gw-np-test-devbox' ) ,
763+ launch_parameters : {
764+ resource_size_request : 'X_SMALL' ,
765+ keep_alive_time_seconds : 300 ,
766+ network_policy_id : networkPolicy . id ,
767+ } ,
768+ gateways : {
769+ RUNLOOP : {
770+ gateway : gatewayConfig . id ,
771+ secret : testSecretName ,
772+ } ,
773+ } ,
774+ } ) ;
775+
776+ // Get gateway URL and token
777+ const urlResult = await devbox . cmd . exec ( 'echo $RUNLOOP_URL' ) ;
778+ gatewayUrl = ( await urlResult . stdout ( ) ) . trim ( ) ;
779+
780+ const tokenResult = await devbox . cmd . exec ( 'echo $RUNLOOP' ) ;
781+ gatewayToken = ( await tokenResult . stdout ( ) ) . trim ( ) ;
782+
783+ expect ( gatewayUrl ) . toBeTruthy ( ) ;
784+ expect ( gatewayToken . startsWith ( 'gws_' ) ) . toBe ( true ) ;
785+ } , MEDIUM_TIMEOUT ) ;
786+
787+ afterAll ( async ( ) => {
788+ if ( devbox ) {
789+ try {
790+ await devbox . shutdown ( ) ;
791+ } catch {
792+ // Ignore
793+ }
794+ }
795+ await cleanUpGatewayConfig ( gatewayConfig ) ;
796+ await cleanUpPolicy ( networkPolicy ) ;
797+ try {
798+ await sdk . api . secrets . delete ( testSecretName ) ;
799+ } catch {
800+ // Ignore
801+ }
802+ } ) ;
803+
804+ test ( 'devbox with network policy and gateway can be created' , async ( ) => {
805+ expect ( devbox ) . toBeDefined ( ) ;
806+ expect ( devbox ! . id ) . toBeTruthy ( ) ;
807+ } ) ;
808+
809+ test ( 'devbox has network policy applied' , async ( ) => {
810+ expect ( devbox ) . toBeDefined ( ) ;
811+ const info = await devbox ! . getInfo ( ) ;
812+ expect ( info . launch_parameters . network_policy_id ) . toBe ( networkPolicy ! . id ) ;
813+ } ) ;
814+
815+ test ( 'devbox has gateway config applied' , async ( ) => {
816+ expect ( devbox ) . toBeDefined ( ) ;
817+ const info = await devbox ! . getInfo ( ) ;
818+ expect ( info . gateway_specs ) . toBeDefined ( ) ;
819+ expect ( info . gateway_specs ?. [ 'RUNLOOP' ] ) . toBeDefined ( ) ;
820+ expect ( info . gateway_specs ?. [ 'RUNLOOP' ] ?. gateway_config_id ) . toBe ( gatewayConfig ! . id ) ;
821+ } ) ;
822+
823+ test ( 'gateway env vars are set correctly' , async ( ) => {
824+ expect ( gatewayUrl ) . toBeTruthy ( ) ;
825+ expect ( gatewayUrl . startsWith ( 'http' ) ) . toBe ( true ) ;
826+ expect ( gatewayToken ) . toBeTruthy ( ) ;
827+ expect ( gatewayToken . startsWith ( 'gws_' ) ) . toBe ( true ) ;
828+ } ) ;
829+
830+ test ( 'network policy info is retrievable' , async ( ) => {
831+ expect ( networkPolicy ) . toBeDefined ( ) ;
832+ const info = await networkPolicy ! . getInfo ( ) ;
833+ expect ( info . id ) . toBe ( networkPolicy ! . id ) ;
834+ expect ( info . egress . allow_all ) . toBe ( true ) ;
835+ } ) ;
836+
837+ test ( 'gateway config info is retrievable' , async ( ) => {
838+ expect ( gatewayConfig ) . toBeDefined ( ) ;
839+ const info = await gatewayConfig ! . getInfo ( ) ;
840+ expect ( info . id ) . toBe ( gatewayConfig ! . id ) ;
841+ expect ( info . auth_mechanism . type ) . toBe ( 'bearer' ) ;
842+ } ) ;
843+
844+ // The following tests are currently skipped because gateway URLs are not
845+ // accessible from devboxes with network policies applied (see comment above)
846+ // Uncomment these when the backend infrastructure is updated to support this.
847+ /*
848+ test('GET request through gateway with network policy', async () => {
849+ const curlCmd = `curl -s -w "\\nHTTP_CODE:%{http_code}" --connect-timeout 30 -H "Authorization: Bearer ${gatewayToken}" "${gatewayUrl}/v1/devboxes?limit=1"`;
850+ const result = await devbox!.cmd.exec(curlCmd);
851+ const output = (await result.stdout()).trim();
852+ const lines = output.split('\n');
853+ const httpCodeLine = lines.pop() || '';
854+ const responseBody = lines.join('\n');
855+ const httpCode = parseInt(httpCodeLine.replace('HTTP_CODE:', ''), 10);
856+
857+ expect(httpCode).toBe(200);
858+ const response = JSON.parse(responseBody);
859+ expect(response).toBeDefined();
860+ expect(Array.isArray(response.devboxes)).toBe(true);
861+ });
862+
863+ test('POST request through gateway with network policy', async () => {
864+ const secretName = uniqueName('gw-np-created-secret');
865+ const curlCmd = `curl -s -w "\\nHTTP_CODE:%{http_code}" --connect-timeout 30 -X POST -H "Authorization: Bearer ${gatewayToken}" -H "Content-Type: application/json" -d '{"name": "${secretName}", "value": "test-value"}' "${gatewayUrl}/v1/secrets"`;
866+ const result = await devbox!.cmd.exec(curlCmd);
867+ const output = (await result.stdout()).trim();
868+ const lines = output.split('\n');
869+ const httpCodeLine = lines.pop() || '';
870+ const httpCode = parseInt(httpCodeLine.replace('HTTP_CODE:', ''), 10);
871+
872+ expect(httpCode).toBe(200);
873+
874+ // Clean up the created secret
875+ try {
876+ await sdk.api.secrets.delete(secretName);
877+ } catch {
878+ // Ignore
879+ }
880+ });
881+ */
882+ } ,
883+ ) ;
714884} ) ;
0 commit comments