@@ -37,6 +37,7 @@ import {
3737 createTestConfig ,
3838 makeMacro ,
3939 initAllState ,
40+ callee ,
4041} from "./testing-utils" ;
4142import {
4243 GitHubVariant ,
@@ -462,6 +463,24 @@ test.serial("load non-existent input", async (t) => {
462463 } ) ;
463464} ) ;
464465
466+ /** A non-empty, but fairly minimal configuration file. */
467+ const simpleConfigFileContents = `
468+ name: my config
469+ queries:
470+ - uses: ./foo_file` ;
471+
472+ /** A less minimal configuration file. */
473+ const otherConfigFileContents = `
474+ name: my config
475+ disable-default-queries: true
476+ queries:
477+ - uses: ./foo
478+ paths-ignore:
479+ - a
480+ - b
481+ paths:
482+ - c/d` ;
483+
465484test . serial ( "load non-empty input" , async ( t ) => {
466485 return await withTmpDir ( async ( tempDir ) => {
467486 setupActionsVars ( tempDir , tempDir ) ;
@@ -476,18 +495,6 @@ test.serial("load non-empty input", async (t) => {
476495 } ,
477496 } ) ;
478497
479- // Just create a generic config object with non-default values for all fields
480- const inputFileContents = `
481- name: my config
482- disable-default-queries: true
483- queries:
484- - uses: ./foo
485- paths-ignore:
486- - a
487- - b
488- paths:
489- - c/d` ;
490-
491498 fs . mkdirSync ( path . join ( tempDir , "foo" ) ) ;
492499
493500 const userConfig : UserConfig = {
@@ -514,7 +521,7 @@ test.serial("load non-empty input", async (t) => {
514521 } ) ;
515522
516523 const languagesInput = "javascript" ;
517- const configFilePath = createConfigFile ( inputFileContents , tempDir ) ;
524+ const configFilePath = createConfigFile ( otherConfigFileContents , tempDir ) ;
518525
519526 const state = initAllState ( ) ;
520527 const actualConfig = await configUtils . initConfig (
@@ -540,14 +547,12 @@ test.serial(
540547 "Using config input and file together, config input should be used." ,
541548 async ( t ) => {
542549 return await withTmpDir ( async ( tempDir ) => {
543- process . env [ "RUNNER_TEMP" ] = tempDir ;
544- process . env [ "GITHUB_WORKSPACE" ] = tempDir ;
550+ setupActionsVars ( tempDir , tempDir ) ;
545551
546- const inputFileContents = `
547- name: my config
548- queries:
549- - uses: ./foo_file` ;
550- const configFilePath = createConfigFile ( inputFileContents , tempDir ) ;
552+ const configFilePath = createConfigFile (
553+ simpleConfigFileContents ,
554+ tempDir ,
555+ ) ;
551556
552557 const configInput = `
553558 name: my config
@@ -576,7 +581,7 @@ test.serial(
576581 // Only JS, python packs will be ignored
577582 const languagesInput = "javascript" ;
578583
579- const state = initAllState ( ) ;
584+ const state = initAllState ( { env : util . getEnv ( ) } ) ;
580585 const config = await configUtils . initConfig (
581586 state ,
582587 createTestInitConfigInputs ( {
@@ -2259,3 +2264,268 @@ test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only r
22592264 { exclude : { tags : "exclude-from-incremental" } } ,
22602265 ] ) ;
22612266} ) ;
2267+
2268+ test ( "determineUserConfig - empty config when neither input is specified" , async ( t ) => {
2269+ await withTmpDir ( async ( tmpDir ) => {
2270+ const target = callee ( configUtils . determineUserConfig )
2271+ . withDefaultActionsEnv ( )
2272+ . withFeatures ( [ ] )
2273+ . withArgs (
2274+ tmpDir ,
2275+ createTestInitConfigInputs ( {
2276+ configInput : undefined ,
2277+ configFile : undefined ,
2278+ workspacePath : tmpDir ,
2279+ } ) ,
2280+ ) ;
2281+
2282+ // The returned configuration should be empty.
2283+ await target
2284+ // The fact that no configuration was provided should have been logged,
2285+ . logs ( t , "No configuration file was provided" )
2286+ // But not the messages for the two input sources
2287+ // or the warning about both inputs.
2288+ . notLogs (
2289+ t ,
2290+ "Using config from action input:" ,
2291+ "Using configuration file:" ,
2292+ "Both a config file and config input were provided. Ignoring config file." ,
2293+ )
2294+ . passes ( t . deepEqual , { } ) ;
2295+ } ) ;
2296+ } ) ;
2297+
2298+ test ( "determineUserConfig - loads config file" , async ( t ) => {
2299+ await withTmpDir ( async ( tmpDir ) => {
2300+ const configFilePath = createConfigFile ( simpleConfigFileContents , tmpDir ) ;
2301+
2302+ const inputs = createTestInitConfigInputs ( {
2303+ configInput : undefined ,
2304+ configFile : configFilePath ,
2305+ workspacePath : tmpDir ,
2306+ } ) ;
2307+ const target = callee ( configUtils . determineUserConfig )
2308+ . withDefaultActionsEnv ( )
2309+ . withArgs ( tmpDir , inputs ) ;
2310+
2311+ await target
2312+ // The path of the input config file should have been logged,
2313+ . logs ( t , `Using configuration file: ${ configFilePath } ` )
2314+ . notLogs (
2315+ t ,
2316+ // The other two origin messages and the warning about both inputs should
2317+ // not have been logged.
2318+ "No configuration file was provided" ,
2319+ "Using config from action input:" ,
2320+ "Both a config file and config input were provided. Ignoring config file." ,
2321+ )
2322+ // The loaded configuration should match `simpleConfigFileContents`.
2323+ . passes ( t . deepEqual , {
2324+ name : "my config" ,
2325+ queries : [ { uses : "./foo_file" } ] ,
2326+ } ) ;
2327+
2328+ // The `configFile` input should not have changed.
2329+ t . is ( inputs . configFile , configFilePath ) ;
2330+ } ) ;
2331+ } ) ;
2332+
2333+ test ( "determineUserConfig - loads config input" , async ( t ) => {
2334+ await withTmpDir ( async ( tmpDir ) => {
2335+ const expectedConfigPath = configUtils . userConfigFromActionPath ( tmpDir ) ;
2336+
2337+ const inputs = createTestInitConfigInputs ( {
2338+ configInput : simpleConfigFileContents ,
2339+ configFile : undefined ,
2340+ workspacePath : tmpDir ,
2341+ } ) ;
2342+ const target = callee ( configUtils . determineUserConfig )
2343+ . withDefaultActionsEnv ( )
2344+ . withArgs ( tmpDir , inputs ) ;
2345+
2346+ await target
2347+ // The input source and path of the generated config file should have been logged.
2348+ . logs (
2349+ t ,
2350+ "Using config from action input:" ,
2351+ `Using configuration file: ${ expectedConfigPath } ` ,
2352+ )
2353+ // The message about no configuration input and
2354+ // the warning about both inputs should not have been logged.
2355+ . notLogs (
2356+ t ,
2357+ "No configuration file was provided" ,
2358+ "Both a config file and config input were provided. Ignoring config file." ,
2359+ )
2360+ // The loaded configuration should match `simpleConfigFileContents`.
2361+ . passes ( t . deepEqual , {
2362+ name : "my config" ,
2363+ queries : [ { uses : "./foo_file" } ] ,
2364+ } ) ;
2365+
2366+ // The `configFile` input should have been mutated to the generated path.
2367+ t . is ( inputs . configFile , expectedConfigPath ) ;
2368+ } ) ;
2369+ } ) ;
2370+
2371+ test ( "determineUserConfig - ignores config file input when both specified" , async ( t ) => {
2372+ await withTmpDir ( async ( tmpDir ) => {
2373+ const configFilePath = createConfigFile ( otherConfigFileContents , tmpDir ) ;
2374+ const expectedConfigPath = configUtils . userConfigFromActionPath ( tmpDir ) ;
2375+
2376+ const inputs = createTestInitConfigInputs ( {
2377+ configInput : simpleConfigFileContents ,
2378+ configFile : configFilePath ,
2379+ workspacePath : tmpDir ,
2380+ } ) ;
2381+ const target = callee ( configUtils . determineUserConfig )
2382+ . withDefaultActionsEnv ( )
2383+ . withArgs ( tmpDir , inputs ) ;
2384+
2385+ await target
2386+ // The path of the generated config file and
2387+ // the warning about both inputs should have been logged.
2388+ . logs (
2389+ t ,
2390+ `Using config from action input: ${ expectedConfigPath } ` ,
2391+ `Using configuration file: ${ expectedConfigPath } ` ,
2392+ "Both a config file and config input were provided. Ignoring config file." ,
2393+ )
2394+ . notLogs ( t , "No configuration file was provided" )
2395+ // The loaded configuration should match `simpleConfigFileContents`.
2396+ . passes ( t . deepEqual , {
2397+ name : "my config" ,
2398+ queries : [ { uses : "./foo_file" } ] ,
2399+ } ) ;
2400+
2401+ // The `configFile` input should have been mutated to the generated path.
2402+ t . is ( inputs . configFile , expectedConfigPath ) ;
2403+ } ) ;
2404+ } ) ;
2405+
2406+ /** A `config` input that we might get from Default Setup. */
2407+ const defaultSetupConfigInput = `
2408+ threat-models: [local, remote]
2409+ default-setup:
2410+ org:
2411+ model-packs: [foo, bar]` ;
2412+
2413+ test ( "determineUserConfig - merges configs if FF is enabled in Default Setup" , async ( t ) => {
2414+ await withTmpDir ( async ( tmpDir ) => {
2415+ const configFilePath = createConfigFile ( simpleConfigFileContents , tmpDir ) ;
2416+ const expectedConfigPath = configUtils . userConfigFromActionPath ( tmpDir ) ;
2417+
2418+ const inputs = createTestInitConfigInputs ( {
2419+ configInput : defaultSetupConfigInput ,
2420+ configFile : configFilePath ,
2421+ workspacePath : tmpDir ,
2422+ } ) ;
2423+ const target = callee ( configUtils . determineUserConfig )
2424+ . withDefaultActionsEnv ( { GITHUB_EVENT_NAME : "dynamic" } )
2425+ . withFeatures ( [ Feature . AllowMergeConfigFiles ] )
2426+ . withArgs ( tmpDir , inputs ) ;
2427+
2428+ // The loaded configuration should match the result of merging
2429+ // `defaultSetupConfigInput` and `simpleConfigFileContents`.
2430+ const expectedConfig = {
2431+ name : "my config" ,
2432+ queries : [ { uses : "./foo_file" } ] ,
2433+ "threat-models" : [ "local" , "remote" ] ,
2434+ "default-setup" : {
2435+ org : {
2436+ "model-packs" : [ "foo" , "bar" ] ,
2437+ } ,
2438+ } ,
2439+ } satisfies UserConfig ;
2440+
2441+ await target
2442+ . logs (
2443+ t ,
2444+ `Using merged configurations from 'config' input with configuration from '${ configFilePath } ': ${ expectedConfigPath } ` ,
2445+ )
2446+ . notLogs (
2447+ t ,
2448+ `Using configuration file: ${ expectedConfigPath } ` ,
2449+ "No configuration file was provided" ,
2450+ `Using config from action input: ${ expectedConfigPath } ` ,
2451+ "Both a config file and config input were provided. Ignoring config file." ,
2452+ )
2453+ . passes ( t . deepEqual , expectedConfig ) ;
2454+
2455+ // The `configFile` input should have been mutated to the generated path.
2456+ t . is ( inputs . configFile , expectedConfigPath ) ;
2457+
2458+ // Since `result` is the result of merging the configurations in-memory,
2459+ // also check whether loading the configuration from disk that was written
2460+ // by `determineUserConfig` matches our expectations.
2461+ const loadedFromDisk = configUtils . getLocalConfig (
2462+ getRunnerLogger ( true ) ,
2463+ expectedConfigPath ,
2464+ false ,
2465+ ) ;
2466+ t . deepEqual ( loadedFromDisk , expectedConfig ) ;
2467+ } ) ;
2468+ } ) ;
2469+
2470+ test ( "determineUserConfig - ignores config file input in Default Setup if FF is off" , async ( t ) => {
2471+ await withTmpDir ( async ( tmpDir ) => {
2472+ const configFilePath = createConfigFile ( otherConfigFileContents , tmpDir ) ;
2473+ const expectedConfigPath = configUtils . userConfigFromActionPath ( tmpDir ) ;
2474+
2475+ const target = callee ( configUtils . determineUserConfig )
2476+ . withDefaultActionsEnv ( { GITHUB_EVENT_NAME : "dynamic" } )
2477+ . withArgs (
2478+ tmpDir ,
2479+ createTestInitConfigInputs ( {
2480+ configInput : simpleConfigFileContents ,
2481+ configFile : configFilePath ,
2482+ workspacePath : tmpDir ,
2483+ } ) ,
2484+ ) ;
2485+
2486+ await target
2487+ . logs (
2488+ t ,
2489+ `Using config from action input: ${ expectedConfigPath } ` ,
2490+ `Using configuration file: ${ expectedConfigPath } ` ,
2491+ "Both a config file and config input were provided. Ignoring config file." ,
2492+ )
2493+ . notLogs ( t , "No configuration file was provided" )
2494+ . passes ( t . deepEqual , {
2495+ name : "my config" ,
2496+ queries : [ { uses : "./foo_file" } ] ,
2497+ } ) ;
2498+ } ) ;
2499+ } ) ;
2500+
2501+ test ( "determineUserConfig - ignores config file input outside Default Setup if FF is on" , async ( t ) => {
2502+ await withTmpDir ( async ( tmpDir ) => {
2503+ const configFilePath = createConfigFile ( otherConfigFileContents , tmpDir ) ;
2504+ const expectedConfigPath = configUtils . userConfigFromActionPath ( tmpDir ) ;
2505+
2506+ const target = callee ( configUtils . determineUserConfig )
2507+ . withDefaultActionsEnv ( )
2508+ . withFeatures ( [ Feature . AllowMergeConfigFiles ] )
2509+ . withArgs (
2510+ tmpDir ,
2511+ createTestInitConfigInputs ( {
2512+ configInput : simpleConfigFileContents ,
2513+ configFile : configFilePath ,
2514+ workspacePath : tmpDir ,
2515+ } ) ,
2516+ ) ;
2517+
2518+ await target
2519+ . logs (
2520+ t ,
2521+ `Using config from action input: ${ expectedConfigPath } ` ,
2522+ `Using configuration file: ${ expectedConfigPath } ` ,
2523+ "Both a config file and config input were provided. Ignoring config file." ,
2524+ )
2525+ . notLogs ( t , "No configuration file was provided" )
2526+ . passes ( t . deepEqual , {
2527+ name : "my config" ,
2528+ queries : [ { uses : "./foo_file" } ] ,
2529+ } ) ;
2530+ } ) ;
2531+ } ) ;
0 commit comments