1- import * as fs from "fs" ;
1+ import fs from "fs" ;
2+ import path from "path" ;
23
34import * as github from "@actions/github" ;
45import test from "ava" ;
@@ -10,7 +11,10 @@ import { GitHubApiDetails } from "./api-client";
1011import * as apiClient from "./api-client" ;
1112import { createStubCodeQL } from "./codeql" ;
1213import { Config } from "./config-utils" ;
13- import { cleanupAndUploadDatabases } from "./database-upload" ;
14+ import {
15+ cleanupAndUploadDatabases ,
16+ uploadBundledDatabase ,
17+ } from "./database-upload" ;
1418import { Feature } from "./feature-flags" ;
1519import * as gitUtils from "./git-utils" ;
1620import { BuiltInLanguage } from "./languages" ;
@@ -23,6 +27,7 @@ import {
2327 getTestEnv ,
2428 initAllState ,
2529 RecordingLogger ,
30+ SAMPLE_DOTCOM_API_DETAILS ,
2631 setupActionsVars ,
2732 setupTests ,
2833} from "./testing-utils" ;
@@ -550,3 +555,86 @@ test.serial(
550555 } ) ;
551556 } ,
552557) ;
558+
559+ test . serial (
560+ "uploadBundledDatabase provides expected request arguments" ,
561+ async ( t ) =>
562+ withTmpDir ( async ( tmpDir ) => {
563+ // Write some file that `uploadBundledDatabase` can use for its `fs.createReadStream(bundledDb)` call
564+ // so we don't have to stub that or create a fake `ReadStream`.
565+ const dbPath = path . join ( tmpDir , "db" ) ;
566+ const content = JSON . stringify ( { } ) ;
567+ fs . writeFileSync ( dbPath , content ) ;
568+
569+ // A simple stub for `fetch` that satisfies Octokit when it inspects the response.
570+ const fetchStub = sinon . stub ( ) . resolves ( {
571+ status : 200 ,
572+ headers : new Headers ( ) ,
573+ text : async ( ) => "" ,
574+ } ) ;
575+
576+ // Construct a real `ApiClient`.
577+ const client = apiClient . getApiClient (
578+ getTestEnv ( ) ,
579+ SAMPLE_DOTCOM_API_DETAILS ,
580+ ) ;
581+
582+ // Then wrap the real `request` method in a stub that calls it with our stubbed `fetch`.
583+ // This ensures that we have a real `ApiClient` with a mostly real `request`, but which
584+ // does not perform any actual requests.
585+ const request = client . request ;
586+ sinon . stub ( client , "request" ) . callsFake ( async ( route , options ) =>
587+ request ( route , {
588+ ...options ,
589+ request : { ...options ?. request , fetch : fetchStub } ,
590+ } ) ,
591+ ) ;
592+
593+ // Stub `getApiClient` to return our modified `ApiClient`.
594+ const getApiClient = sinon
595+ . stub ( apiClient , "getApiClient" )
596+ . returns ( client ) ;
597+
598+ // Calling `uploadBundledDatabase` shouldn't throw.
599+ const nwo : RepositoryNwo = { owner : "test" , repo : "foo" } ;
600+ const language : BuiltInLanguage = BuiltInLanguage . java ;
601+
602+ await t . notThrowsAsync ( async ( ) => {
603+ await uploadBundledDatabase (
604+ nwo ,
605+ language ,
606+ "123" ,
607+ dbPath ,
608+ content . length ,
609+ SAMPLE_DOTCOM_API_DETAILS ,
610+ ) ;
611+ } ) ;
612+
613+ // Check that our stubs were called and that they received the expected arguments.
614+ t . is ( getApiClient . callCount , 1 ) ;
615+ t . is ( fetchStub . callCount , 1 ) ;
616+
617+ const args = fetchStub . args [ 0 ] ;
618+ t . is ( args . length , 2 ) ;
619+
620+ const url = args [ 0 ] ;
621+ t . is (
622+ url ,
623+ `https://uploads.github.com/repos/${ nwo . owner } /${ nwo . repo } /code-scanning/codeql/databases/${ language } ?name=${ language } -database&commit_oid=123` ,
624+ ) ;
625+
626+ const options = args [ 1 ] ;
627+ t . like ( options , {
628+ // Going via `new Headers` and `Object.fromEntries` ensures we get the same cases
629+ // for the header keys as we are given.
630+ headers : Object . fromEntries (
631+ new Headers ( {
632+ authorization : `token ${ SAMPLE_DOTCOM_API_DETAILS . auth } ` ,
633+ "Content-Type" : "application/zip" ,
634+ "Content-Length" : content . length . toString ( ) ,
635+ } ) . entries ( ) ,
636+ ) ,
637+ method : "POST" ,
638+ } satisfies RequestInit ) ;
639+ } ) ,
640+ ) ;
0 commit comments