Skip to content

Commit

Permalink
test type checks with ensureValue
Browse files Browse the repository at this point in the history
  • Loading branch information
kswanson33 committed Dec 16, 2024
1 parent d155ae2 commit b7c1b55
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 16 deletions.
3 changes: 2 additions & 1 deletion src/lib/aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
GetResourcesCommandOutput,
ResourceGroupsTaggingAPIClient,
} from '@aws-sdk/client-resource-groups-tagging-api'
import { ensureValueWithExchange } from './utils'

export const RUN_ID_TAG_KEY = 'runId'
export const TITLE_TAG_KEY = 'title'
Expand Down Expand Up @@ -46,7 +47,7 @@ export async function registerECSTaskDefinition(
// The following defines a new family versus versioning the base family

const containerDefinition = baseTaskDefinition.containerDefinitions?.map((container) => {
const environment = container.environment || []
const environment = ensureValueWithExchange(container.environment, [])

environment.push({
name: 'TRUSTED_OUTPUT_ENDPOINT',
Expand Down
35 changes: 21 additions & 14 deletions src/lib/run-studies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
getTaskDefinitionsWithRunId,
deleteECSTaskDefinitions,
} from './aws'
import { filterManagementAppRuns, filterOrphanTaskDefinitions } from './utils'
import { ensureValueWithExchange, ensureValueWithError, filterManagementAppRuns, filterOrphanTaskDefinitions } from './utils'
import { managementAppGetRunnableStudiesRequest, toaGetRunsRequest } from './api'
import 'dotenv/config'
import { ManagementAppGetRunnableStudiesResponse } from './types'
Expand All @@ -32,10 +32,10 @@ async function launchStudy(
{ key: TITLE_TAG_KEY, value: studyTitle },
]
const baseTaskDefinitionData = await getECSTaskDefinition(client, baseTaskDefinition)

if (baseTaskDefinitionData.taskDefinition === undefined) {
throw new Error(`Could not find task definition data for ${baseTaskDefinition}`)
}
baseTaskDefinitionData.taskDefinition = ensureValueWithError(
baseTaskDefinitionData.taskDefinition,
`Could not find task definition data for ${baseTaskDefinition}`,
)

const newTaskDefinitionFamily = `${baseTaskDefinitionData.taskDefinition.family}-${runId}`

Expand All @@ -47,10 +47,14 @@ async function launchStudy(
imageLocation,
taskTags,
)

if (registerTaskDefResponse.taskDefinition?.family === undefined) {
throw new Error('Generated task definition has undefined family')
}
registerTaskDefResponse.taskDefinition = ensureValueWithError(
registerTaskDefResponse.taskDefinition,
`Could not register task definition ${newTaskDefinitionFamily}`,
)
registerTaskDefResponse.taskDefinition.family = ensureValueWithError(
registerTaskDefResponse.taskDefinition.family,
'Generated task definition has undefined family',
)

return await runECSFargateTask(
client,
Expand Down Expand Up @@ -78,7 +82,10 @@ async function cleanupTaskDefs(
ecsClient: ECSClient,
) {
// Garbage collect orphan task definitions
const taskDefsWithRunId = (await getTaskDefinitionsWithRunId(taggingClient)).ResourceTagMappingList || []
const taskDefsWithRunId = ensureValueWithExchange(
(await getTaskDefinitionsWithRunId(taggingClient)).ResourceTagMappingList,
[],
)
const orphanTaskDefinitions = filterOrphanTaskDefinitions(bmaResults, taskDefsWithRunId)
await deleteECSTaskDefinitions(ecsClient, orphanTaskDefinitions)
}
Expand All @@ -88,10 +95,10 @@ export async function runStudies(options: { ignoreAWSRuns: boolean }): Promise<v
const taggingClient = new ResourceGroupsTaggingAPIClient()

// Set in IaC
const cluster = process.env.ECS_CLUSTER || ''
const baseTaskDefinition = process.env.BASE_TASK_DEFINITION_FAMILY || ''
const subnets = process.env.VPC_SUBNETS || ''
const securityGroup = process.env.SECURITY_GROUP || ''
const cluster = ensureValueWithExchange(process.env.ECS_CLUSTER, '')
const baseTaskDefinition = ensureValueWithExchange(process.env.BASE_TASK_DEFINITION_FAMILY, '')
const subnets = ensureValueWithExchange(process.env.VPC_SUBNETS, '')
const securityGroup = ensureValueWithExchange(process.env.SECURITY_GROUP, '')

const bmaRunnablesResults = await managementAppGetRunnableStudiesRequest()
const toaGetRunsResult = await toaGetRunsRequest()
Expand Down
28 changes: 27 additions & 1 deletion src/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { filterManagementAppRuns, filterOrphanTaskDefinitions } from '../lib/utils'
import { filterManagementAppRuns, filterOrphanTaskDefinitions, ensureValueWithError, ensureValueWithExchange } from '../lib/utils'

describe('filterManagementAppRuns', () => {
it('filters out runs in the TOA', () => {
Expand Down Expand Up @@ -91,3 +91,29 @@ describe('filterOrphanTaskDefinitions', () => {
expect(filterOrphanTaskDefinitions(mockManagementAppResponse, mockTaskDefResources)).toStrictEqual(['arn2'])
})
})

describe('ensureValueWithError', () => {
it('makes sure values are defined', () => {
expect(ensureValueWithError(10)).toBe(10)
})

it('responds with the given error message if values are undefined', () => {
expect(() => ensureValueWithError(null, "Can't be null or undefined")).toThrowError("Can't be null or undefined")
expect(() => ensureValueWithError(undefined)).toThrowError()
})
})

describe('ensureValueWithExchange', () => {
it('makes sure values are defined', () => {
const myValue: number | undefined = 10
expect(ensureValueWithExchange(myValue, 0)).toBe(10)
})

it('exchanges the value if undefined', () => {
const myObject: { myStringArray: string[] | undefined; myNumber: number } = {
myStringArray: undefined,
myNumber: 10,
}
expect(ensureValueWithExchange(myObject.myStringArray, ['my string'])).toEqual(['my string'])
})
})
16 changes: 16 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,19 @@ export const filterOrphanTaskDefinitions = (

return orphanTaskDefinitions
}

// returns given value with type certainty, or errors if value is null or undefined
export const ensureValueWithError = <T>(value: T, message?: string): NonNullable<T> => {
if (value === null || value === undefined) {
throw new Error(message)
}
return value
}

// returns given value with type certainty, or silently exchanges value if null or undefined
export const ensureValueWithExchange = <T>(value: T, exchange: NonNullable<T>): NonNullable<T> => {
if (value === null || value === undefined) {
return exchange
}
return value
}

0 comments on commit b7c1b55

Please sign in to comment.