From d4eca999ce703c655846a5539a242826da07a522 Mon Sep 17 00:00:00 2001 From: Mikael Vesavuori Date: Sun, 11 Jun 2023 21:19:17 +0200 Subject: [PATCH] feat(v3): simplify change SHA management; use median lead time for change instead of average value --- .gitignore | 3 +- README.md | 27 ++---- SECURITY.md | 2 +- deployment.sh | 37 +------- jest.env.js | 2 +- package.json | 2 +- src/domain/services/Dorametrix.ts | 85 ++++++------------- src/domain/valueObjects/Deployment.ts | 10 +-- src/domain/valueObjects/Event.ts | 25 +++--- src/infrastructure/adapters/web/AddEvent.ts | 2 +- .../repositories/DynamoDbRepository.ts | 4 +- src/interfaces/CleanedItem.ts | 4 +- src/interfaces/Deployment.ts | 13 +-- src/interfaces/Event.ts | 8 +- testdata/database/DynamoTestDatabase.ts | 52 ++++++------ testdata/database/LocalTestDatabase.ts | 47 +++++----- tests/unit/application/getParser.test.ts | 14 +-- tests/unit/domain/valueObjects/Change.test.ts | 6 +- .../domain/valueObjects/Deployment.test.ts | 6 +- .../unit/domain/valueObjects/Incident.test.ts | 6 +- tests/unit/usecases/getLastDeployment.test.ts | 4 +- tests/unit/usecases/getMetrics.test.ts | 10 +-- 22 files changed, 140 insertions(+), 229 deletions(-) diff --git a/.gitignore b/.gitignore index cfa62c5..fa49927 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Files .env .dccache +.DS_Store # Folders node_modules/ @@ -16,4 +17,4 @@ jest-coverage/ # Diagrams diagrams/*.bkp diagrams/*.bak -diagrams/*.dtmp \ No newline at end of file +diagrams/*.dtmp diff --git a/README.md b/README.md index 9bebc8d..3254e2f 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ As seen above, the required inputs are: 1. The endpoint 2. The API key -3. The product name +3. The repo name (proxy for the "product") #### GitHub Actions action @@ -213,7 +213,7 @@ steps: fetch-depth: 0 - name: Dorametrix - uses: mikaelvesavuori/dorametrix-action@v1.0.0 + uses: mikaelvesavuori/dorametrix-action@v3 with: endpoint: ${{ secrets.DORAMETRIX_ENDPOINT }} api-key: ${{ secrets.DORAMETRIX_API_KEY }} @@ -225,6 +225,8 @@ The specific action, `mikaelvesavuori/dorametrix-action@v1.0.0`, is available fo #### Bitbucket Pipelines pipe +_Note that the version below assumes Dorametrix version 1/2._ + An example using two user-provided secrets and setting the product with a known variable representing the repo name: ```yaml @@ -389,7 +391,7 @@ All of the below demonstrates "directly calling" the API; since webhook events f ```json { "eventType": "change", - "product": "demo" + "repo": "demo" } ``` @@ -406,21 +408,8 @@ All of the below demonstrates "directly calling" the API; since webhook events f ```json { "eventType": "deployment", - "product": "demo", - "changes": [ - { - "id": "356a192b7913b04c54574d18c28d46e6395428ab", - "timeCreated": "1642879177" - }, - { - "id": "da4b9237bacccdf19c0760cab7aec4a8359010b0", - "timeCreated": "1642874964" - }, - { - "id": "77de68daecd823babbb58edb1c8e14d7106e83bb", - "timeCreated": "1642873353" - } - ] + "repo": "demo", + "changeSha": "356a192b7913b04c54574d18c28d46e6395428ab" } ``` @@ -437,7 +426,7 @@ All of the below demonstrates "directly calling" the API; since webhook events f ```json { "eventType": "incident", - "product": "demo" + "repo": "demo" } ``` diff --git a/SECURITY.md b/SECURITY.md index 35d4dcf..6de49f4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ These versions of `dorametrix` are currently being supported with security updat | Version | Supported | | ------- | ------------------ | -| 1.x.x | :white_check_mark: | +| 3.x.x | :white_check_mark: | | 0.x.x | :x: | ## Reporting a Vulnerability diff --git a/deployment.sh b/deployment.sh index 903db8e..5f2f1e8 100644 --- a/deployment.sh +++ b/deployment.sh @@ -31,40 +31,7 @@ if [ -z "$REPO" ]; then echo "Dorametrix error: REPO is not set! Exiting..." && CURRENT_GIT_SHA=$(git log --pretty=format:'%H' -n 1) echo "ℹ️ CURRENT_GIT_SHA --> $CURRENT_GIT_SHA" -# Get commit ID of last production deployment -LAST_PROD_DEPLOY=$(curl "$ENDPOINT/lastdeployment?product=$REPO_NAME" -H 'Authorization: "$API_KEY"' | jq '.id' -r) - -# If no LAST_PROD_DEPLOY is found, then very defensively assume that the first commit is most recent deployment -if [[ -z "$LAST_PROD_DEPLOY" ]] || [[ "$LAST_PROD_DEPLOY" == "null" ]]; then - echo "⚠️ Dorametrix warning: Could not find a value for LAST_PROD_DEPLOY. Setting LAST_PROD_DEPLOY to the value of the first commit." - LAST_PROD_DEPLOY=$(git rev-list HEAD | tail -n 1) -fi -echo "ℹ️ LAST_PROD_DEPLOY --> $LAST_PROD_DEPLOY" - -echo "Verifying that commits exist..." -if ! git --no-pager log $LAST_PROD_DEPLOY..$CURRENT_GIT_SHA --decorate=short --pretty=oneline; then - echo "🔥 Dorametrix error: Unable to find the expected commits in working tree! Exiting..." - exit 1 -fi - -# Get all commits between current work and last production deployment then put result in local TXT file -git log $LAST_PROD_DEPLOY..$CURRENT_GIT_SHA --pretty=format:'{%n ^^^^id^^^^: ^^^^%H^^^^,%n ^^^^timeCreated^^^^: ^^^^%ct^^^^%n },' | sed 's/"/\\"/g' | sed 's/\^^^^/"/g' | sed "$ s/,$//" | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' | awk 'BEGIN { print("[") } { print($0) } END { print("]") }' >commits.json - -# Use TXT output to set variable with list of commits -CHANGES=$(cat commits.json | jq '[.[] | { id: .id, timeCreated: .timeCreated }]') -echo "ℹ️ CHANGES --> $CHANGES" -CHANGES_LENGTH=$(echo $CHANGES | jq '. | length' -r) -echo "ℹ️ CHANGES_LENGTH --> $CHANGES_LENGTH" - -# Remove the scratch TXT file -rm commits.json - -if [[ $CHANGES_LENGTH -eq 0 ]]; then - echo "🔥 Dorametrix error: No changes detected. Exiting..." - exit 1 -fi - # Call Dorametrix and create deployment event with Git changes -curl -X POST $ENDPOINT/event?authorization="$API_KEY" -d '{ "eventType": "deployment", "repo": "'$REPO_NAME'", "changes": '"$CHANGES"' }' -H "Content-Type: application/json" +curl -X POST $ENDPOINT/event?authorization="$API_KEY" -d '{ "eventType": "deployment", "repo": "'$REPO_NAME'", "changeSha": '"$CURRENT_GIT_SHA"' }' -H "Content-Type: application/json" -echo -e "\n✅ Dorametrix deployment script has finished successfully!" \ No newline at end of file +echo -e "\n✅ Dorametrix deployment script has finished successfully!" diff --git a/jest.env.js b/jest.env.js index bb52bd8..6fd5c95 100644 --- a/jest.env.js +++ b/jest.env.js @@ -1 +1 @@ -process.env.IS_TEST = true; \ No newline at end of file +process.env.IS_TEST = true; diff --git a/package.json b/package.json index 2674e24..af64d31 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dorametrix", "description": "Dorametrix is a Node.js-based service that helps you calculate your DORA metrics, by inferring your metrics from events you can create manually or with webhooks", - "version": "2.2.2", + "version": "3.0.0", "author": "Mikael Vesavuori", "license": "MIT", "keywords": [ diff --git a/src/domain/services/Dorametrix.ts b/src/domain/services/Dorametrix.ts index 54fa70e..827fd99 100644 --- a/src/domain/services/Dorametrix.ts +++ b/src/domain/services/Dorametrix.ts @@ -2,7 +2,7 @@ import { getDiffInSeconds, prettifyTime } from 'chrono-utils'; import { Dorametrix } from '../../interfaces/Dorametrix'; import { Change } from '../../interfaces/Change'; -import { Deployment, DeploymentChange } from '../../interfaces/Deployment'; +import { Deployment } from '../../interfaces/Deployment'; import { Incident } from '../../interfaces/Incident'; import { DeploymentResponse } from '../../interfaces/DeploymentResponse'; @@ -27,32 +27,12 @@ class DorametrixConcrete implements Dorametrix { * @description Get the commit ID for the last deployment to production. */ public getLastDeployment(lastDeployment: Deployment): DeploymentResponse { - if (lastDeployment?.changes) { - const changes: DeploymentChange[] = lastDeployment?.changes; - - // Get latest deployment - const deploymentTimes = changes - .map((change) => change.timeCreated) - .sort() - .reverse(); - const latestTime = deploymentTimes[0]; - - // Get the ID of the latest deployment - const matchingChange = changes.filter( - (change: DeploymentChange) => change.timeCreated === latestTime - ); - - if (matchingChange && matchingChange.length > 0) { - const { id } = matchingChange[0]; - - // If the timestamp uses a 10-digit format, add zeroes to be in line with how JavaScript does it - const timeCreated = latestTime.length === 10 ? latestTime + '000' : latestTime; - - return { - id, - timeCreated - }; - } + if (lastDeployment?.id && lastDeployment?.timeCreated) { + const { id, timeCreated } = lastDeployment; + return { + id, + timeCreated + }; } return { @@ -84,51 +64,42 @@ class DorametrixConcrete implements Dorametrix { } /** - * @description Get the averaged lead time for a change getting into production (deployment). + * @description Get the median lead time for a change getting into production (deployment). */ public getLeadTimeForChanges(changes: Change[], deployments: Deployment[]): string { if (deployments.length === 0) return '00:00:00:00'; - let accumulatedTime = 0; - deployments.forEach( - (deployment: Deployment) => (accumulatedTime += this.calculateLeadTime(deployment, changes)) - ); + const accumulatedTimes = deployments + .map((deployment: Deployment) => this.calculateLeadTime(deployment, changes)) + .sort(); - return prettifyTime(accumulatedTime / deployments.length); + const medianPoint = + accumulatedTimes.length < 2 ? 0 : Math.floor(accumulatedTimes.length / 2) - 1; + const medianValue = accumulatedTimes[medianPoint] || 0; + + return prettifyTime(medianValue); } /** * @description Calculate the lead time of a change for an individual deployment. */ - private calculateLeadTime(deployment: Deployment, allChanges: Change[]): number { - const { changes, timeCreated } = deployment; - - /** - * Each change might lead to one or more deployments, so go and get each one. - */ - const changeIds = changes.map((change: DeploymentChange) => change.id); - const matches = allChanges - .filter((change: DeploymentChange) => changeIds.includes(change.id)) - .map((change: DeploymentChange) => change.timeCreated) - .sort((a: any, b: any) => a.timeCreated - b.timeCreated); + private calculateLeadTime(deployment: Deployment, changes: Change[]): number { + const { changeSha, timeCreated } = deployment; - /** - * Calculate diff between earliest commit timestamp (`firstMatch`) and deployment timestamp (`timeCreated`). - */ - if (matches?.length > 0) { - const firstMatch = matches[0]; + const commitTimeCreated = changes + .filter((change: Change) => change.id === changeSha) + .map((change: Change) => change.timeCreated)[0]; - if (firstMatch && timeCreated && firstMatch > timeCreated) { - console.warn( - `Unexpected deployment data: firstMatch field is later than timeCreated...Skipping it.\n--> timeCreated: ${firstMatch} firstMatch: ${firstMatch}` - ); - return 0; - } + if (!commitTimeCreated) return 0; - return getDiffInSeconds(firstMatch, timeCreated); + if (commitTimeCreated > timeCreated) { + console.warn( + `Unexpected deployment data: Commit timeCreated is later than deployment timeCreated...Skipping it.\n--> Deployment: ${timeCreated} Commit: ${commitTimeCreated}` + ); + return 0; } - return 0; + return getDiffInSeconds(commitTimeCreated, timeCreated); } /** diff --git a/src/domain/valueObjects/Deployment.ts b/src/domain/valueObjects/Deployment.ts index c5d3def..af7b71e 100644 --- a/src/domain/valueObjects/Deployment.ts +++ b/src/domain/valueObjects/Deployment.ts @@ -1,6 +1,6 @@ import { getCurrentDate } from 'chrono-utils'; -import { Deployment, DeploymentChange } from '../../interfaces/Deployment'; +import { Deployment } from '../../interfaces/Deployment'; import { Event } from '../../interfaces/Event'; import { @@ -22,11 +22,11 @@ class DeploymentConcrete { date: string; eventType: string; id: string; - changes: DeploymentChange[]; + changeSha: string; timeCreated: string; constructor(deploymentEvent: Event) { - const { repo, id, eventType, timeCreated, changes } = deploymentEvent; + const { repo, id, eventType, timeCreated, changeSha } = deploymentEvent; if (!repo) throw new MissingRepoNameError( @@ -45,7 +45,7 @@ class DeploymentConcrete { this.date = getCurrentDate(true); this.eventType = eventType; this.id = id; - this.changes = changes || []; + this.changeSha = changeSha || ''; this.timeCreated = timeCreated.toString(); } @@ -55,7 +55,7 @@ class DeploymentConcrete { date: this.date, eventType: this.eventType, id: this.id, - changes: this.changes, + changeSha: this.changeSha, timeCreated: this.timeCreated }); } diff --git a/src/domain/valueObjects/Event.ts b/src/domain/valueObjects/Event.ts index 03bebf1..3d6cd20 100644 --- a/src/domain/valueObjects/Event.ts +++ b/src/domain/valueObjects/Event.ts @@ -1,7 +1,6 @@ import { getCurrentDate } from 'chrono-utils'; import { Event, EventType } from '../../interfaces/Event'; -import { Change } from '../../interfaces/Change'; import { Parser } from '../../interfaces/Parser'; /** @@ -13,17 +12,17 @@ export async function makeEvent( body: Record, headers: Record ): Promise { - const eventConcrete = new EventConcrete(parser, body, headers); - await EventConcrete.populate(eventConcrete, parser, body, headers); + const eventConcrete = new DorametrixEvent(parser, body, headers); + await DorametrixEvent.populate(eventConcrete, parser, body, headers); return eventConcrete.getDTO(); } -class EventConcrete { +class DorametrixEvent { repo = ''; date: string; eventType = ''; id = ''; - changes: Change[] = []; + changeSha = ''; eventTime = ''; timeCreated = ''; timeResolved = ''; @@ -32,15 +31,15 @@ class EventConcrete { constructor(parser: Parser, body: Record, headers: Record) { this.date = getCurrentDate(true); - EventConcrete.populate(this, parser, body, headers); + DorametrixEvent.populate(this, parser, body, headers); } static async populate( - event: EventConcrete, + event: DorametrixEvent, parser: Parser, body: Record, headers: Record - ): Promise { + ): Promise { const eventType = await parser.getEventType({ body, headers }); const repo = parser.getRepoName(body); @@ -48,7 +47,7 @@ class EventConcrete { event.repo = repo; event.date = getCurrentDate(true); event.eventType = eventType; - event.changes = body.changes || []; + event.changeSha = body.changeSha || ''; // TODO: Needed? Is same as some other ID...? const { id, eventTime, timeCreated, timeResolved, title, message } = await parser.getPayload({ body, @@ -69,9 +68,9 @@ class EventConcrete { parser: Parser, body: Record, headers: Record - ): Promise { - const event = new EventConcrete(parser, body, headers); - return EventConcrete.populate(event, parser, body, headers); + ): Promise { + const event = new DorametrixEvent(parser, body, headers); + return DorametrixEvent.populate(event, parser, body, headers); } public getDTO(): Event { @@ -80,7 +79,7 @@ class EventConcrete { date: this.date, eventType: this.eventType as EventType, id: this.id, - changes: this.changes, + changeSha: this.changeSha, eventTime: this.eventTime, timeCreated: this.timeCreated, timeResolved: this.timeResolved, diff --git a/src/infrastructure/adapters/web/AddEvent.ts b/src/infrastructure/adapters/web/AddEvent.ts index fb72924..7c30645 100644 --- a/src/infrastructure/adapters/web/AddEvent.ts +++ b/src/infrastructure/adapters/web/AddEvent.ts @@ -34,7 +34,7 @@ export async function handler( const headers = event.headers; const repo = createNewDynamoDbRepository(); - const parser = await getParser(headers); + const parser = getParser(headers); const metricEvent = await makeEvent(parser, body, headers); await createEvent(repo, metricEvent); diff --git a/src/infrastructure/repositories/DynamoDbRepository.ts b/src/infrastructure/repositories/DynamoDbRepository.ts index 64629c2..91f5f51 100644 --- a/src/infrastructure/repositories/DynamoDbRepository.ts +++ b/src/infrastructure/repositories/DynamoDbRepository.ts @@ -169,7 +169,7 @@ class DynamoRepository implements Repository { * @description Handle (create/update) a Deployment in the repository. */ public async addDeployment(deployment: Deployment): Promise { - const { repo, id, changes, timeCreated } = deployment; + const { repo, id, changeSha, timeCreated } = deployment; const command = { TableName: this.tableName, @@ -178,7 +178,7 @@ class DynamoRepository implements Repository { sk: { S: timeCreated }, expiresAt: { S: getExpiryTime() }, timeCreated: { S: timeCreated }, - changes: { S: JSON.stringify(changes) }, + changeSha: { S: JSON.stringify(changeSha) }, id: { S: id } } }; diff --git a/src/interfaces/CleanedItem.ts b/src/interfaces/CleanedItem.ts index e1756d1..7606aa7 100644 --- a/src/interfaces/CleanedItem.ts +++ b/src/interfaces/CleanedItem.ts @@ -1,5 +1,3 @@ -import { DeploymentChange } from './Deployment'; - /** * @description Item from database that has been cleaned and conformed. */ @@ -7,7 +5,7 @@ export type CleanedItem = { id?: string; timeCreated?: string; timeResolved?: string; - changes?: DeploymentChange[]; + changeSha?: string; title?: string; message?: string; }; diff --git a/src/interfaces/Deployment.ts b/src/interfaces/Deployment.ts index c01769d..ab15f7b 100644 --- a/src/interfaces/Deployment.ts +++ b/src/interfaces/Deployment.ts @@ -6,14 +6,9 @@ export type Deployment = { repo: string; timeCreated: string; id: string; - changes: DeploymentChange[]; + /** + * Refers to the commit SHA that led to this deployment. + */ + changeSha: string; date: string; }; - -/** - * @description Representation of a change inside the Deployment. - */ -export type DeploymentChange = { - id: string; - timeCreated: string; -}; diff --git a/src/interfaces/Event.ts b/src/interfaces/Event.ts index 1b51c56..6a353c1 100644 --- a/src/interfaces/Event.ts +++ b/src/interfaces/Event.ts @@ -1,5 +1,3 @@ -import { Change } from './Change'; - /** * @description The Event is the primitive, raw event type. */ @@ -25,11 +23,11 @@ export interface Event { id: string; /** - * @description Array of changes. Applicable if - * `eventType` is `deployment`. Will stay empty otherwise. + * @description SHA of commit that led to this deployment. + * Applicable only if `eventType` is `deployment`, will stay empty otherwise. * @note Only for deployments. */ - changes: Change[]; + changeSha: string; /** * @description Unix timestamp of event time. diff --git a/testdata/database/DynamoTestDatabase.ts b/testdata/database/DynamoTestDatabase.ts index 37746c0..5ad4138 100644 --- a/testdata/database/DynamoTestDatabase.ts +++ b/testdata/database/DynamoTestDatabase.ts @@ -1,37 +1,37 @@ /** * @description Change items as stored in DynamoDB. */ -export const testChangeItem = { +export const testChangeItems = { Items: [ { - timeCreated: { S: '1673104450286' }, - sk: { S: '1672104450286' }, + timeCreated: { S: '1641034800000' }, + sk: { S: '1641034800000' }, pk: { S: 'CHANGE_SOMEORG/SOMEREPO' }, - id: { S: 'bdc284af8b859959457b0ac2d82f5547f96345bb' } + id: { S: '73ea2c4b72ed28ebf7f00e785fc4fc1c7365139e' } }, { - timeCreated: { S: '1673104450455' }, - sk: { S: '1672104450455' }, + timeCreated: { S: '1640784652540' }, + sk: { S: '1640784652540' }, pk: { S: 'CHANGE_SOMEORG/SOMEREPO' }, - id: { S: 'a11f3ccab218e1218e04b69a70e7f351bfd104f7' } + id: { S: 'df211ccdd94a63e0bcb9e6ae427a249484a49d60' } }, { - timeCreated: { S: '1673104450201' }, - sk: { S: '1673104450201' }, + timeCreated: { S: '1641038400000' }, + sk: { S: '1641038400000' }, pk: { S: 'CHANGE_SOMEORG/SOMEREPO' }, - id: { S: '2a7bd974a431b994732d7b0295878fb2bd3e821b' } + id: { S: '94b98567fa5abfe9c003fbb9cb541f1735cefff0' } }, { - timeCreated: { S: '1673104450412' }, - sk: { S: '1673104450412' }, + timeCreated: { S: '1640784652530' }, + sk: { S: '1640784652530' }, pk: { S: 'CHANGE_SOMEORG/SOMEREPO' }, - id: { S: 'f14af16a77ac735d441ea6c96a30c2ad3f17d6a8' } + id: { S: '2eebe17407138ae15d7cbfff2a79a051d3770a64' } }, { - timeCreated: { S: '1673104450591' }, - sk: { S: '1673104450591' }, + timeCreated: { S: '1641039310000' }, + sk: { S: '1641039310000' }, pk: { S: 'CHANGE_SOMEORG/SOMEREPO' }, - id: { S: '650996873306355e22bdb13ff8e035e3748a7956' } + id: { S: '5a8c1b761edc95512a0083f35454915304cc9498' } } ] }; @@ -39,16 +39,14 @@ export const testChangeItem = { /** * @description Deployment item as stored in DynamoDB. */ -export const testDeploymentItem = { +export const testDeploymentItems = { Items: [ { - timeCreated: { S: '1673104694874' }, - sk: { S: '1673104694874' }, + timeCreated: { S: '1641039900000' }, + sk: { S: '1641039900000' }, pk: { S: 'DEPLOYMENT_SOMEORG/SOMEREPO' }, id: { S: '4e594023e1642a161820951d25970301c38b3728' }, - changes: { - S: '[{"id":"2a7bd974a431b994732d7b0295878fb2bd3e821b","timeCreated":"1673020000"},{"id":"a8fa8e40-122d-4741-a9f1-b70f813b7e1b","timeCreated":"1642879177"},{"id":"6b2467d5-0d91-4945-af6b-483aaf762f56","timeCreated":"1642874964"},{"id":"eb6dad65-9e9c-44c8-ad6a-da0ca4ba9e98","timeCreated":"1642873353"}]' - } + changeSha: { S: '73ea2c4b72ed28ebf7f00e785fc4fc1c7365139e' } } ] }; @@ -56,14 +54,14 @@ export const testDeploymentItem = { /** * @description Incident item as stored in DynamoDB. */ -export const testIncidentItem = { +export const testIncidentItems = { Items: [ { timeResolved: { S: '1671945000000' }, timeCreated: { S: '1670945000000' }, sk: { S: '1670945000000' }, pk: { S: 'INCIDENT_SOMEORG/SOMEREPO' }, - id: { S: '1091505287' }, + id: { S: 'e4ea294c062c525643df036a35ca579b905fa400' }, title: { S: 'some test issue' } } ] @@ -112,9 +110,9 @@ export function getCachedTestData(key: string, fromDate: string, toDate: string) export function getTestData(key: string) { const fixedKey = key.toUpperCase(); - if (fixedKey.startsWith('CHANGE_')) return testChangeItem; - if (fixedKey.startsWith('DEPLOYMENT_')) return testDeploymentItem; - if (fixedKey.startsWith('INCIDENT_')) return testIncidentItem; + if (fixedKey.startsWith('CHANGE_')) return testChangeItems; + if (fixedKey.startsWith('DEPLOYMENT_')) return testDeploymentItems; + if (fixedKey.startsWith('INCIDENT_')) return testIncidentItems; return { Items: [] }; } diff --git a/testdata/database/LocalTestDatabase.ts b/testdata/database/LocalTestDatabase.ts index 22b1488..13bf560 100644 --- a/testdata/database/LocalTestDatabase.ts +++ b/testdata/database/LocalTestDatabase.ts @@ -8,21 +8,16 @@ export const deployments: Deployment[] = [ timeCreated: '1641039900000', // Sat Jan 01 2022 13:25:00 GMT+0100 (CET) date: '20220101', eventType: 'deployment', - id: '987236hfahc82', - changes: [ - { - id: '5a8c1b761edc95512a0083f35454915304cc9498', - timeCreated: '1641039310000' - }, - { - id: '94b98567fa5abfe9c003fbb9cb541f1735cefff0', - timeCreated: '1641038400000' - }, - { - id: '73ea2c4b72ed28ebf7f00e785fc4fc1c7365139e', - timeCreated: '1641034800000' - } - ] + id: '3705e751cf44780483a2c3df5327dd61777ef697', + changeSha: '94b98567fa5abfe9c003fbb9cb541f1735cefff0' + }, + { + repo: 'SOMEORG/SOMEREPO', + timeCreated: '1641038410000', // Sat Jan 01 2022 13:00:00 GMT+0100 (CET) + date: '20220101', + eventType: 'deployment', + id: '743f328470810c2dfbfbc0d7b9a17c0c22af5e96', + changeSha: '94b98567fa5abfe9c003fbb9cb541f1735cefff0' } ]; @@ -31,35 +26,35 @@ export const changes: Change[] = [ repo: 'SOMEORG/SOMEREPO', id: '73ea2c4b72ed28ebf7f00e785fc4fc1c7365139e', timeCreated: '1641034800000', // Sat Jan 01 2022 12:00:00 GMT+0100 (CET) - date: '20230101', + date: '20220101', eventType: 'deployment' }, { repo: 'SOMEORG/SOMEREPO', - id: 'abc123', + id: 'ae3fe7b03bffe4c2d5b8973eff72675373c88302', timeCreated: '1640784652540', - date: '20230101', + date: '20220101', eventType: 'deployment' }, { repo: 'SOMEORG/SOMEREPO', id: '94b98567fa5abfe9c003fbb9cb541f1735cefff0', timeCreated: '1641038400000', // Sat Jan 01 2022 13:00:00 GMT+0100 (CET) - date: '20230101', + date: '20220101', eventType: 'deployment' }, { repo: 'SOMEORG/SOMEOTHERREPO', - id: 'qwerty123', + id: 'bc81466e46f874d658101817602e9bb47d8e3f4e', timeCreated: '1640784652530', - date: '20230101', + date: '20220101', eventType: 'deployment' }, { repo: 'SOMEORG/SOMEREPO', id: '5a8c1b761edc95512a0083f35454915304cc9498', timeCreated: '1641039310000', // Sat Jan 01 2022 13:15:10 GMT+0100 (CET) - date: '20230101', + date: '20220101', eventType: 'deployment' } ]; @@ -67,19 +62,19 @@ export const changes: Change[] = [ export const incidents: Incident[] = [ { repo: 'SOMEORG/SOMEREPO', - id: '19028dj1klaf2', + id: '4f5724887853c77ba8e2136af8e35a5840b6be52', timeCreated: '1641034800000', // Sat Jan 01 2022 12:00:00 GMT+0100 (CET) timeResolved: '1641132310000', // Sun Jan 02 2022 15:05:10 GMT+0100 (CET) - date: '20230101', + date: '20220101', eventType: 'deployment', title: 'Some title' }, { repo: 'SOMEORG/SOMEOTHERREPO', - id: 'jau387fauigag8736', + id: '907451a5ac22fbddf5111dfe1c3d3d27351148d1', timeCreated: '1641034200000', // Sat Jan 01 2022 10:50:00 GMT+0000 timeResolved: '1641131310000', // Sun Jan 02 2022 13:48:30 GMT+0000 - date: '20230101', + date: '20220101', eventType: 'deployment', title: 'Some title' } diff --git a/tests/unit/application/getParser.test.ts b/tests/unit/application/getParser.test.ts index 42b87ab..53e0219 100644 --- a/tests/unit/application/getParser.test.ts +++ b/tests/unit/application/getParser.test.ts @@ -7,35 +7,35 @@ import { JiraParser } from '../../../src/application/parsers/JiraParser'; describe('Success cases', () => { test('It should get the GitHub parser', async () => { - const parser = await getParser({ + const parser = getParser({ 'User-Agent': 'GitHub' }); expect(parser).toBeInstanceOf(GitHubParser); }); test('It should get the Jira parser', async () => { - const parser = await getParser({ + const parser = getParser({ 'User-Agent': 'Atlassian' }); expect(parser).toBeInstanceOf(JiraParser); }); test('It should get the Bitbucket parser', async () => { - const parser = await getParser({ + const parser = getParser({ 'User-Agent': 'Bitbucket' }); expect(parser).toBeInstanceOf(BitbucketParser); }); test('It should get the direct parser', async () => { - const parser = await getParser({ + const parser = getParser({ 'User-Agent': 'something_else' }); expect(parser).toBeInstanceOf(DirectParser); }); test('It should get the direct parser with lower-case user-agent headers', async () => { - const parser = await getParser({ + const parser = getParser({ 'user-agent': 'something_else' }); expect(parser).toBeInstanceOf(DirectParser); @@ -43,7 +43,7 @@ describe('Success cases', () => { test('It should get the direct parser if missing User-Agent headers', async () => { // @ts-ignore - const parser = await getParser(); + const parser = getParser(); expect(parser).toBeInstanceOf(DirectParser); }); @@ -52,7 +52,7 @@ describe('Success cases', () => { process.env.SHORTCUT_REPONAME = 'A'; process.env.SHORTCUT_INCIDENT_LABEL_ID = '1'; - const parser = await getParser({ + const parser = getParser({ 'User-Agent': 'Apache-HttpClient', 'Shortcut-Signature': '---' }); diff --git a/tests/unit/domain/valueObjects/Change.test.ts b/tests/unit/domain/valueObjects/Change.test.ts index 03b5b27..ae777f7 100644 --- a/tests/unit/domain/valueObjects/Change.test.ts +++ b/tests/unit/domain/valueObjects/Change.test.ts @@ -24,7 +24,7 @@ describe('Failure cases', () => { makeChange({ eventType: 'deployment', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -41,7 +41,7 @@ describe('Failure cases', () => { makeChange({ repo: 'something', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -58,7 +58,7 @@ describe('Failure cases', () => { makeChange({ repo: 'something', eventType: 'deployment', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', diff --git a/tests/unit/domain/valueObjects/Deployment.test.ts b/tests/unit/domain/valueObjects/Deployment.test.ts index b8163a4..67a95b9 100644 --- a/tests/unit/domain/valueObjects/Deployment.test.ts +++ b/tests/unit/domain/valueObjects/Deployment.test.ts @@ -16,7 +16,7 @@ describe('Failure cases', () => { makeDeployment({ eventType: 'deployment', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -33,7 +33,7 @@ describe('Failure cases', () => { makeDeployment({ repo: 'something', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -50,7 +50,7 @@ describe('Failure cases', () => { makeDeployment({ repo: 'something', eventType: 'deployment', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', diff --git a/tests/unit/domain/valueObjects/Incident.test.ts b/tests/unit/domain/valueObjects/Incident.test.ts index 31b7fc0..5e12e1e 100644 --- a/tests/unit/domain/valueObjects/Incident.test.ts +++ b/tests/unit/domain/valueObjects/Incident.test.ts @@ -26,7 +26,7 @@ describe('Failure cases', () => { makeIncident({ eventType: 'deployment', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -43,7 +43,7 @@ describe('Failure cases', () => { makeIncident({ repo: 'something', id: 'something', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', @@ -60,7 +60,7 @@ describe('Failure cases', () => { makeIncident({ repo: 'something', eventType: 'deployment', - changes: [], + changeSha: '', eventTime: '', timeCreated: '', timeResolved: '', diff --git a/tests/unit/usecases/getLastDeployment.test.ts b/tests/unit/usecases/getLastDeployment.test.ts index d9eb47c..3551e05 100644 --- a/tests/unit/usecases/getLastDeployment.test.ts +++ b/tests/unit/usecases/getLastDeployment.test.ts @@ -10,8 +10,8 @@ describe('Success cases', () => { const result = await getLastDeployment(repo, input); expect(result).toMatchObject({ - id: '5a8c1b761edc95512a0083f35454915304cc9498', - timeCreated: '1641039310000' + id: '3705e751cf44780483a2c3df5327dd61777ef697', + timeCreated: '1641039900000' }); }); diff --git a/tests/unit/usecases/getMetrics.test.ts b/tests/unit/usecases/getMetrics.test.ts index d4d37ea..d3ba652 100644 --- a/tests/unit/usecases/getMetrics.test.ts +++ b/tests/unit/usecases/getMetrics.test.ts @@ -30,13 +30,13 @@ describe('Success cases', () => { to: '20221231' }, total: { - deploymentCount: 1, + deploymentCount: 2, incidentCount: 1 }, metrics: { - changeFailureRate: '1.00', - deploymentFrequency: '1.00', - leadTimeForChanges: '00:01:25:00', + changeFailureRate: '0.50', + deploymentFrequency: '2.00', + leadTimeForChanges: '00:00:00:10', timeToRestoreServices: '01:03:05:10' } }; @@ -72,7 +72,7 @@ describe('Success cases', () => { metrics: { changeFailureRate: '1.00', deploymentFrequency: '1.00', - leadTimeForChanges: '00:00:04:04', + leadTimeForChanges: '00:01:25:00', timeToRestoreServices: '11:13:46:40' } };