From e01424dc5670cfb47185b9448224a22de8954084 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Tue, 4 Jun 2024 07:42:31 +0900 Subject: [PATCH 1/9] Move function for generating mocked cost data to `testUtils.ts` Acked-by: Akira Hayashi --- src/cost.test.ts | 56 +++------------------------------------------- src/testUtils.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 src/testUtils.ts diff --git a/src/cost.test.ts b/src/cost.test.ts index 21f1572..726b073 100644 --- a/src/cost.test.ts +++ b/src/cost.test.ts @@ -1,6 +1,7 @@ import AWS from 'aws-sdk'; import { AWSConfig } from './config'; import { getRawCostByService, getTotalCosts } from './cost'; +import { generateMockedCostByService } from './testUtils' import AWSMock from 'aws-sdk-mock'; import dayjs from 'dayjs'; @@ -10,57 +11,6 @@ const costDataLength = 65; const fixedToday = '2024-05-11'; // cost of 'this month' will be sum of 10 days from May 1 to May 10 ('today' is omitted because its cost is incomplete) const fixedFirstDay = dayjs(fixedToday).subtract(costDataLength, 'day'); -const generateMockPricingData = () => { - const resultsByTime = []; - for (let i = 0; i < costDataLength; i++) { - const date = dayjs(fixedFirstDay).add(i, 'day').format('YYYY-MM-DD'); - const month = dayjs(date).month(); // 0-indexed (0 = January, 1 = February, etc.) - let service1Cost; - - switch (month) { - case 2: // March - service1Cost = 0.9; - break; - case 3: // April - service1Cost = 1.0; // Total cost of service1 in April will be 30.00 - break; - case 4: // May - service1Cost = 1.1; - break; - default: - service1Cost = 0.0; // Default cost if none of the above - } - - resultsByTime.push({ - TimePeriod: { - Start: date, - End: dayjs(date).add(1, 'day').format('YYYY-MM-DD'), - }, - Groups: [ - { - Keys: ['service1'], - Metrics: { - UnblendedCost: { - Amount: String(service1Cost), - Unit: 'USD', - }, - }, - }, - { - Keys: ['service2'], - Metrics: { - UnblendedCost: { - Amount: String(service1Cost * 100), - Unit: 'USD', - }, - }, - }, - ], - }); - } - return { ResultsByTime: resultsByTime }; -}; - describe('Cost Functions', () => { beforeAll(() => { AWSMock.setSDKInstance(AWS); @@ -90,7 +40,7 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockPricingData = generateMockPricingData(); + const mockPricingData = generateMockedCostByService(fixedToday, costDataLength); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { callback(null, mockPricingData); @@ -142,7 +92,7 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockPricingData = generateMockPricingData(); + const mockPricingData = generateMockedCostByService(fixedToday, costDataLength); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { callback(null, mockPricingData); diff --git a/src/testUtils.ts b/src/testUtils.ts new file mode 100644 index 0000000..2cc2d25 --- /dev/null +++ b/src/testUtils.ts @@ -0,0 +1,58 @@ +import dayjs from 'dayjs'; + +const generateMockedCostByService = (fixedToday: string, costDataLength: number) => { + const fixedFirstDay = dayjs(fixedToday).subtract(costDataLength, 'day'); + + const resultsByTime = []; + for (let i = 0; i < costDataLength; i++) { + const date = dayjs(fixedFirstDay).add(i, 'day').format('YYYY-MM-DD'); + const month = dayjs(date).month(); // 0-indexed (0 = January, 1 = February, etc.) + let service1Cost; + + switch (month) { + case 2: // March + service1Cost = 0.9; + break; + case 3: // April + service1Cost = 1.0; // Total cost of service1 in April will be 30.00 + break; + case 4: // May + service1Cost = 1.1; + break; + default: + service1Cost = 0.0; // Default cost if none of the above + } + + resultsByTime.push({ + TimePeriod: { + Start: date, + End: dayjs(date).add(1, 'day').format('YYYY-MM-DD'), + }, + Groups: [ + { + Keys: ['service1'], + Metrics: { + UnblendedCost: { + Amount: String(service1Cost), + Unit: 'USD', + }, + }, + }, + { + Keys: ['service2'], + Metrics: { + UnblendedCost: { + Amount: String(service1Cost * 100), + Unit: 'USD', + }, + }, + }, + ], + }); + } + return { ResultsByTime: resultsByTime }; +}; + +if (process.env.NODE_ENV === 'test') { + module.exports = { generateMockedCostByService }; +} From 115a2b270cb9ee67a298ed100184a8f421ac9b37 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Tue, 4 Jun 2024 07:45:16 +0900 Subject: [PATCH 2/9] Rename `mockPricingData` to `mockedPricingData` --- src/cost.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cost.test.ts b/src/cost.test.ts index 726b073..dec4192 100644 --- a/src/cost.test.ts +++ b/src/cost.test.ts @@ -40,10 +40,10 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockPricingData = generateMockedCostByService(fixedToday, costDataLength); + const mockedPricingData = generateMockedCostByService(fixedToday, costDataLength); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { - callback(null, mockPricingData); + callback(null, mockedPricingData); }); const rawCostByService = await getRawCostByService(awsConfig); @@ -92,10 +92,10 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockPricingData = generateMockedCostByService(fixedToday, costDataLength); + const mockedPricingData = generateMockedCostByService(fixedToday, costDataLength); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { - callback(null, mockPricingData); + callback(null, mockedPricingData); }); const totalCosts = await getTotalCosts(awsConfig); From 11674a8e4cc91acd5831df69a35668e7e0b9e648 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Tue, 4 Jun 2024 07:45:56 +0900 Subject: [PATCH 3/9] Format codes --- src/cost.test.ts | 12 +++++++++--- src/testUtils.ts | 13 ++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/cost.test.ts b/src/cost.test.ts index dec4192..7504401 100644 --- a/src/cost.test.ts +++ b/src/cost.test.ts @@ -1,7 +1,7 @@ import AWS from 'aws-sdk'; import { AWSConfig } from './config'; import { getRawCostByService, getTotalCosts } from './cost'; -import { generateMockedCostByService } from './testUtils' +import { generateMockedCostByService } from './testUtils'; import AWSMock from 'aws-sdk-mock'; import dayjs from 'dayjs'; @@ -40,7 +40,10 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockedPricingData = generateMockedCostByService(fixedToday, costDataLength); + const mockedPricingData = generateMockedCostByService( + fixedToday, + costDataLength, + ); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { callback(null, mockedPricingData); @@ -92,7 +95,10 @@ describe('Cost Functions', () => { region: 'us-east-1', }; - const mockedPricingData = generateMockedCostByService(fixedToday, costDataLength); + const mockedPricingData = generateMockedCostByService( + fixedToday, + costDataLength, + ); AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { callback(null, mockedPricingData); diff --git a/src/testUtils.ts b/src/testUtils.ts index 2cc2d25..8ae86f8 100644 --- a/src/testUtils.ts +++ b/src/testUtils.ts @@ -1,10 +1,13 @@ import dayjs from 'dayjs'; -const generateMockedCostByService = (fixedToday: string, costDataLength: number) => { - const fixedFirstDay = dayjs(fixedToday).subtract(costDataLength, 'day'); +const generateMockedCostByService = ( + fixedToday: string, + costDataLength: number, +) => { + const fixedFirstDay = dayjs(fixedToday).subtract(costDataLength, 'day'); - const resultsByTime = []; - for (let i = 0; i < costDataLength; i++) { + const resultsByTime = []; + for (let i = 0; i < costDataLength; i++) { const date = dayjs(fixedFirstDay).add(i, 'day').format('YYYY-MM-DD'); const month = dayjs(date).month(); // 0-indexed (0 = January, 1 = February, etc.) let service1Cost; @@ -54,5 +57,5 @@ const generateMockedCostByService = (fixedToday: string, costDataLength: number) }; if (process.env.NODE_ENV === 'test') { - module.exports = { generateMockedCostByService }; + module.exports = { generateMockedCostByService }; } From 38b8624b45b507045f80dd1aa9fe2e42bca9f334 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 03:39:18 +0900 Subject: [PATCH 4/9] Express 'specific-period summary' function as test --- jest.config.cjs | 4 ++ src/cost.ts | 6 +++ src/printers/slack.test.ts | 78 ++++++++++++++++++++++++++++++++++++++ src/printers/slack.ts | 6 +++ 4 files changed, 94 insertions(+) create mode 100644 src/printers/slack.test.ts diff --git a/jest.config.cjs b/jest.config.cjs index 63e27fa..a7b47db 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -10,6 +10,10 @@ var esmModules = [ 'is-interactive', 'stdin-discarder', 'aws-sdk-client-mock', + 'node-fetch', + 'data-uri-to-buffer', + 'fetch-blob', + 'formdata-polyfill', ]; module.exports = { diff --git a/src/cost.ts b/src/cost.ts index 8cce24e..9046427 100644 --- a/src/cost.ts +++ b/src/cost.ts @@ -180,3 +180,9 @@ export async function getTotalCosts(awsConfig: AWSConfig): Promise { return totals; } + +if (process.env.NODE_ENV === 'test') { + Object.assign(module.exports, { + calculateServiceTotals, + }); +} diff --git a/src/printers/slack.test.ts b/src/printers/slack.test.ts new file mode 100644 index 0000000..fd602ca --- /dev/null +++ b/src/printers/slack.test.ts @@ -0,0 +1,78 @@ +import AWS from 'aws-sdk'; +import { AWSConfig } from './config'; +import { formatServiceBreakdown } from './slack'; +import { generateMockedCostByService } from '../testUtils'; +import { getTotalCosts } from '../cost'; +import AWSMock from 'aws-sdk-mock'; + +const costDataLength = 65; +const fixedToday = '2024-05-11'; // cost of 'this month' will be sum of 10 days from May 1 to May 10 ('today' is omitted because its cost is incomplete) + +const awsConfig: AWSConfig = { + credentials: { + accessKeyId: 'testAccessKeyId', + secretAccessKey: 'testSecretAccessKey', + sessionToken: 'testSessionToken', + }, + region: 'us-east-1', +}; + +const mockedCostByService = generateMockedCostByService( + fixedToday, + costDataLength, +); + +beforeAll(() => { + AWSMock.setSDKInstance(AWS); +}); + +afterAll(() => { + AWSMock.restore(); +}); + +beforeEach(() => { + jest.useFakeTimers('modern'); + jest.setSystemTime(new Date(fixedToday).getTime()); +}); + +afterEach(() => { + jest.useRealTimers(); +}); + +AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { + callback(null, mockedCostByService); +}); + +describe('formatServiceBreakdown', () => { + it('should return service breakdown of "yesterday" by default', async () => { + const totalCosts = await getTotalCosts(awsConfig); + const result = formatServiceBreakdown(totalCosts); + + // cost value is defined in generateMockedCostByService + expect(result).toEqual('> service2: `$110.00`\n' + '> service1: `$1.10`'); + }); + + it('should return service breakdown of "Last 7 days"', async () => { + const totalCosts = await getTotalCosts(awsConfig); + const result = formatServiceBreakdown(totalCosts, 'last7Days'); + + // cost value is defined in generateMockedCostByService + expect(result).toEqual('> service2: `$770.00`\n' + '> service1: `$7.70`'); + }); + + it('should return service breakdown of "This Month"', async () => { + const totalCosts = await getTotalCosts(awsConfig); + const result = formatServiceBreakdown(totalCosts, 'thisMonth'); + + // cost value is defined in generateMockedCostByService + expect(result).toEqual('> service2: `$1100.00`\n' + '> service1: `$11.00`'); + }); + + it('should return service breakdown of "Last Month"', async () => { + const totalCosts = await getTotalCosts(awsConfig); + const result = formatServiceBreakdown(totalCosts, 'lastMonth'); + + // cost value is defined in generateMockedCostByService + expect(result).toEqual('> service2: `$3000.00`\n' + '> service1: `$30.00`'); + }); +}); diff --git a/src/printers/slack.ts b/src/printers/slack.ts index ec88ba6..277f516 100644 --- a/src/printers/slack.ts +++ b/src/printers/slack.ts @@ -79,3 +79,9 @@ ${formatServiceBreakdown(costs)} }, }); } + +if (process.env.NODE_ENV === 'test') { + Object.assign(module.exports, { + formatServiceBreakdown, + }); +} From cbbdadb2a027698e0a26284650b3c90c67e5616b Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 03:47:51 +0900 Subject: [PATCH 5/9] Add 'specific-period summary' function --- src/printers/slack.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/printers/slack.ts b/src/printers/slack.ts index 277f516..bfed812 100644 --- a/src/printers/slack.ts +++ b/src/printers/slack.ts @@ -7,18 +7,26 @@ import { TotalCosts } from '../cost'; * @param costs Cost breakdown for account * @returns formatted message */ -function formatServiceBreakdown(costs: TotalCosts): string { - const serviceCosts = costs.totalsByService; +function formatServiceBreakdown(costs: TotalCosts, period: string): string { + const validPeriods = ['yesterday', 'last7Days', 'thisMonth', 'lastMonth']; + + if (!validPeriods.includes(period)) { + throw new Error( + '"period" must be one of "yesterday", "thisMonth", or "lastMonth"', + ); + } + + const serviceCosts = costs.totalsByService[period]; - const sortedServices = Object.keys(serviceCosts.yesterday) - .filter((service) => serviceCosts.yesterday[service] > 0) - .sort((a, b) => serviceCosts.yesterday[b] - serviceCosts.yesterday[a]); + const sortedServices = Object.keys(serviceCosts) + .filter((service) => serviceCosts[service] > 0) + .sort((a, b) => serviceCosts[b] - serviceCosts[a]); - const serviceCostsYesterday = sortedServices.map((service) => { - return `> ${service}: \`$${serviceCosts.yesterday[service].toFixed(2)}\``; + const serviceCostsFormatted = sortedServices.map((service) => { + return `> ${service}: \`$${serviceCosts[service].toFixed(2)}\``; }); - return serviceCostsYesterday.join('\n'); + return serviceCostsFormatted.join('\n'); } export async function notifySlack( From 152ee56306e8d51cce9bb79d989113fc34f6b283 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 04:28:56 +0900 Subject: [PATCH 6/9] Use 'specific-period summary' function with default (='yesterday') --- Makefile | 3 ++- readme.md | 1 + src/index.ts | 6 ++++++ src/printers/slack.ts | 3 ++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 29c1da3..8dc8ba8 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ BUILD_DIR = ./dist SRC_DIR = ./src NODE_MODULES = ./node_modules SRC_FILES := $(shell find $(SRC_DIR) -type f) +BREAKDOWN_PERIOD ?= "yesterday" .PHONY: all install build run @@ -18,7 +19,7 @@ $(BUILD_DIR): $(SRC_FILES) $(NODE_MODULES) build: $(BUILD_DIR) run: build - node ./bin/index.js --profile "${AWS_PROFILE}" --access-key "${ACCESS_KEY}" --secret-key "${SECRET_KEY}" --role-arn "${ROLE_ARN}" --region "${AWS_REGION}" -S $(SLACK_TOKEN_AWS_COST_CLI) -C $(SLACK_CHANNEL_ID) + node ./bin/index.js --profile "${AWS_PROFILE}" --access-key "${ACCESS_KEY}" --secret-key "${SECRET_KEY}" --role-arn "${ROLE_ARN}" --region "${AWS_REGION}" -S $(SLACK_TOKEN_AWS_COST_CLI) -C $(SLACK_CHANNEL_ID) --breakdown-period $(BREAKDOWN_PERIOD) test: pnpm test diff --git a/readme.md b/readme.md index 1228dde..47853a5 100644 --- a/readme.md +++ b/readme.md @@ -47,6 +47,7 @@ $ aws-cost --help -S, --slack-token [token] Slack token for the slack message -C, --slack-channel [channel] Slack channel to post the message to + -P, --breakdown-period [period] Unit period to show service breakdown (yesterday|last7Days|thisMonth|lastMonth)') -v, --version Get the version of the CLI -h, --help Get the help of the CLI diff --git a/src/index.ts b/src/index.ts index d02b977..02bf02c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,10 @@ program '-C, --slack-channel [channel]', 'Channel to which the slack integration should post', ) + .option( + '-P, --breakdown-period [period]', + 'Unit period to show service breakdown (yesterday|last7Days|thisMonth|lastMonth)', + ) // Other options .option('-v, --version', 'Get the version of the CLI') .option('-h, --help', 'Get the help of the CLI') @@ -58,6 +62,7 @@ type OptionsType = { // Slack token slackToken: string; slackChannel: string; + breakdownPeriod: string; // Other options help: boolean; }; @@ -98,5 +103,6 @@ if (options.slackToken && options.slackChannel) { options.summary, options.slackToken, options.slackChannel, + options.breakdownPeriod, ); } diff --git a/src/printers/slack.ts b/src/printers/slack.ts index bfed812..f351d04 100644 --- a/src/printers/slack.ts +++ b/src/printers/slack.ts @@ -35,6 +35,7 @@ export async function notifySlack( isSummary: boolean, slackToken: string, slackChannel: string, + period: string = 'yesterday', ) { const channel = slackChannel; @@ -59,7 +60,7 @@ export async function notifySlack( const breakdown = ` > *Breakdown by Service:* -${formatServiceBreakdown(costs)} +${formatServiceBreakdown(costs, period)} `; let message = `${summary}`; From a21584fd2c56b6acbeaa19337347fcf8401501e0 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 04:41:00 +0900 Subject: [PATCH 7/9] Add period notation to notify message header --- src/printers/slack.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/printers/slack.ts b/src/printers/slack.ts index f351d04..ef378ad 100644 --- a/src/printers/slack.ts +++ b/src/printers/slack.ts @@ -58,8 +58,30 @@ export async function notifySlack( > Total Last Month: \`$${totals.lastMonth.toFixed(2)}\` `; + let periodNotation: string; + switch (period) { + case 'yesterday': + periodNotation = 'Yesterday'; + break; + + case 'last7Days': + periodNotation = 'Last 7 Days'; + break; + + case 'thisMonth': + periodNotation = 'This Month'; + break; + + case 'lastMonth': + periodNotation = 'Last Month'; + break; + default: + periodNotation = 'Yesterday'; + break; + } + const breakdown = ` -> *Breakdown by Service:* +> *Breakdown by Service* (${periodNotation}): ${formatServiceBreakdown(costs, period)} `; From a376d437f484e65950a03140f5444d312373ba48 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 04:58:14 +0900 Subject: [PATCH 8/9] Remove indent from `readme.md` --- readme.md | 70 +++++++++++++++++++------------------- src/printers/slack.test.ts | 4 +-- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/readme.md b/readme.md index 47853a5..05e28fc 100644 --- a/readme.md +++ b/readme.md @@ -217,47 +217,47 @@ If you need to use Role-Based Access Control (RBAC), you will need to configure 1. **Provider Role** - This role provides the necessary permissions for `aws-cost-cli`. It requires the above permissions policy and the following trust policy. - - **Trust Policy** - - Replace `arn:aws:iam::YOUR_ACCOUNT_ID:role/YourConsumerRole` with the ARN of the consumer role. - - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::YOUR_ACCOUNT_ID:role/YourConsumerRole" - }, - "Action": "sts:AssumeRole" - } - ] - } - ``` +This role provides the necessary permissions for `aws-cost-cli`. It requires the above permissions policy and the following trust policy. + +**Trust Policy** + +Replace `arn:aws:iam::YOUR_ACCOUNT_ID:role/YourConsumerRole` with the ARN of the consumer role. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::YOUR_ACCOUNT_ID:role/YourConsumerRole" + }, + "Action": "sts:AssumeRole" + } + ] +} +``` 2. **Consumer Role** - This role is used by the user or service (such as GitHub Actions) that needs to assume the provider role to access cost information. It requires the following permissions policy. +This role is used by the user or service (such as GitHub Actions) that needs to assume the provider role to access cost information. It requires the following permissions policy. - **Permissions Policy** +**Permissions Policy** - Replace `arn:aws:iam::YOUR_ACCOUNT_ID:role/YourProviderRole` with the ARN of the provider role. +Replace `arn:aws:iam::YOUR_ACCOUNT_ID:role/YourProviderRole` with the ARN of the provider role. - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "sts:AssumeRole", - "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/YourProviderRole" - } - ] - } - ``` +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/YourProviderRole" + } + ] +} +``` Please also note that this tool uses AWS Cost Explorer under the hood which [costs $0.01 per request](https://aws.amazon.com/aws-cost-management/aws-cost-explorer/pricing/). diff --git a/src/printers/slack.test.ts b/src/printers/slack.test.ts index fd602ca..2ad7878 100644 --- a/src/printers/slack.test.ts +++ b/src/printers/slack.test.ts @@ -44,9 +44,9 @@ AWSMock.mock('CostExplorer', 'getCostAndUsage', (params, callback) => { }); describe('formatServiceBreakdown', () => { - it('should return service breakdown of "yesterday" by default', async () => { + it('should return service breakdown of "yesterday"', async () => { const totalCosts = await getTotalCosts(awsConfig); - const result = formatServiceBreakdown(totalCosts); + const result = formatServiceBreakdown(totalCosts, 'yesterday'); // cost value is defined in generateMockedCostByService expect(result).toEqual('> service2: `$110.00`\n' + '> service1: `$1.10`'); From 87083aedbf61ab23f844b03fa73353e2d2701553 Mon Sep 17 00:00:00 2001 From: Akira Hayashi Date: Wed, 5 Jun 2024 05:01:25 +0900 Subject: [PATCH 9/9] Unify parens into curly braces --- Makefile | 2 +- src/cost.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8dc8ba8..0ee651b 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ $(BUILD_DIR): $(SRC_FILES) $(NODE_MODULES) build: $(BUILD_DIR) run: build - node ./bin/index.js --profile "${AWS_PROFILE}" --access-key "${ACCESS_KEY}" --secret-key "${SECRET_KEY}" --role-arn "${ROLE_ARN}" --region "${AWS_REGION}" -S $(SLACK_TOKEN_AWS_COST_CLI) -C $(SLACK_CHANNEL_ID) --breakdown-period $(BREAKDOWN_PERIOD) + node ./bin/index.js --profile "${AWS_PROFILE}" --access-key "${ACCESS_KEY}" --secret-key "${SECRET_KEY}" --role-arn "${ROLE_ARN}" --region "${AWS_REGION}" -S "${SLACK_TOKEN_AWS_COST_CLI}" -C "${SLACK_CHANNEL_ID}" --breakdown-period "${BREAKDOWN_PERIOD}" test: pnpm test diff --git a/src/cost.test.ts b/src/cost.test.ts index 7504401..dc6f24f 100644 --- a/src/cost.test.ts +++ b/src/cost.test.ts @@ -134,6 +134,8 @@ describe('Cost Functions', () => { }, }; + console.log(expectedTotalCosts); + const roundToTwoDecimals = (num: number) => Math.round(num * 100) / 100; Object.keys(totalCosts.totals).forEach((key) => {