Skip to content

Commit

Permalink
Add comprehensive API tests for coin change calculation
Browse files Browse the repository at this point in the history
This update introduces a set of API tests to ensure the correct functioning of the application's coin change calculation feature. Various edge cases are considered such as multiple cash denominations, large target values, the absence of unit coins, the target being smaller than the smallest coin, and negative cash value. It also includes a workflow for GitHub Actions to automate these test checks.
  • Loading branch information
Jegors Čemisovs committed Apr 16, 2024
0 parents commit b4906f8
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 0 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/api-tests-change.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This workflow runs the API tests for the bruno application on the latest version of Ubuntu

name: API Tests - Change

on:
workflow_dispatch:
permissions:
contents: read
actions: read
checks: write
jobs:
run_change_api_test:
name: API Tests - Change
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download ijhttp
run: curl -f -s -L -o ijhttp.zip https://jb.gg/ijhttp/latest
- name: Unpack ijhttp
run: unzip -nq ijhttp.zip
- name: Remove ijhttp.zip
run: rm ijhttp.zip
- name: Run API tests
run: ijhttp/ijhttp --env azure --env-file http-client.env.json change/*.http --report

- name: Publish Test Report
uses: dorny/test-reporter@v1
if: success() || failure() # run this step even if previous step failed
with:
name: Change API Tests # Name of the check run which will be created
path: reports/report.xml # Path to test results
reporter: java-junit # Format of test results
12 changes: 12 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
ISC License

Copyright 2024 Jegors Čemisovs

Permission to use, copy, modify, and/or distribute this software for any purpose with or without
fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 changes: 24 additions & 0 deletions change/A greedy approach is not optimal.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
### A greedy approach is not optimal
# This test case checks if the API correctly returns the fewest number of coins for a target of 20 cents.
# The available coins are 1, 10, and 11 cents. The expected result is two 10 cent coins.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
10,
11
],
"target": 20
}

> {%
client.test("A greedy approach is not optimal", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 2, "Number of coins is not 2");
client.assert(body.fewestCoins.filter(coin => coin === 10).length === 2, "The coins do not include two 10 cent coins");
})
%}
25 changes: 25 additions & 0 deletions change/Another possible change without unit coins available.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### Another possible change without unit coins available
# This test case checks if the API correctly returns the fewest number of coins for a target of 27 cents.
# The available coins are 4 and 5 cents. The expected result is three 4 cent coins and three 5 cent coins.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
4,
5
],
"target": 27
}

> {%
client.test("Another possible change without unit coins available", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 6, "Number of coins is not 6");
client.assert(body.fewestCoins.filter(coin => coin === 4).length === 3, "The coins do not include three 4 cent coins");
client.assert(body.fewestCoins.filter(coin => coin === 5).length === 3, "The coins do not include three 5 cent coins");
})
%}

23 changes: 23 additions & 0 deletions change/Cannot find negative change values.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### Cannot find negative change values
# This test case checks if the API correctly returns an error when the target is a negative value.
# The available coins are 1, 2, and 5 cents. The target is -5 cents. The expected result is an error message.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
2,
5
],
"target": -5
}

> {%
client.test("Cannot find negative change values", function () {
client.assert(response.status === 400, "Response status is not 400");
let body = response.body;
client.assert(body.error === "Negative change not allowed", "Error message is incorrect");
})
%}
28 changes: 28 additions & 0 deletions change/Change for 1 cent.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### Change for 1 cent
# This test case checks if the API correctly returns the fewest number of coins for a target of 1 cent.
# The available coins are 1, 5, 10, and 25 cents. The expected result is a single 1 cent coin.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
5,
10,
25
],
"target": 1
}

> {%
client.test("Change for 1 cent", function () {
client.assert(response.status === 200, "Response status is not 200");
const type = response.contentType.mimeType;
client.assert(type === "application/json", `Expected 'application/json' but received '${type}'`);
let body = response.body;
client.assert(body.fewestCoins.length === 1, "Number of coins is not 1");
client.assert(body.fewestCoins[0] === 1, "The coin is not 1 cent");
})
%}

27 changes: 27 additions & 0 deletions change/Change with Lilliputian Coins.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### Change with Lilliputian Coins
# This test case checks if the API correctly returns the fewest number of coins for a target of 23 cents.
# The available coins are 1, 4, 15, 20, and 50 cents. The expected result is two 4 cent coins and one 15 cent coin.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
4,
15,
20,
50
],
"target": 23
}

> {%
client.test("Change with Lilliputian Coins", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 3, "Number of coins is not 3");
client.assert(body.fewestCoins.filter(coin => coin === 4).length === 2, "The coins do not include two 4 cent coins");
client.assert(body.fewestCoins.includes(15), "The coins do not include a 15 cent coin");
})
%}
26 changes: 26 additions & 0 deletions change/Change with Lower Elbonia Coins.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
### Change with Lower Elbonia Coins
# This test case checks if the API correctly returns the fewest number of coins for a target of 63 cents.
# The available coins are 1, 5, 10, 21, and 25 cents. The expected result is three 21 cent coins.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
5,
10,
21,
25
],
"target": 63
}

> {%
client.test("Change with Lower Elbonia Coins", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 3, "Number of coins is not 3");
client.assert(body.fewestCoins.filter(coin => coin === 21).length === 3, "The coins do not include three 21 cent coins");
})
%}
22 changes: 22 additions & 0 deletions change/Error if no combination can add up to target.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
### Error if no combination can add up to target
# This test case checks if the API correctly returns an error when no combination of available coins can add up to the target.
# The available coins are 5 and 10 cents. The target is 94 cents. The expected result is an error message.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
5,
10
],
"target": 94
}

> {%
client.test("Error if no combination can add up to target", function () {
client.assert(response.status === 400, "Response status is not 400");
let body = response.body;
client.assert(body.error === "can't make target with given coins", "Error message is incorrect");
})
%}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
### Error testing for change smaller than the smallest of coins
# This test case checks if the API correctly returns an error when the target is smaller than the smallest available coin.
# The available coins are 5 and 10 cents. The target is 3 cents. The expected result is an error message.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
5,
10
],
"target": 3
}

> {%
client.test("Error testing for change smaller than the smallest of coins", function () {
client.assert(response.status === 400, "Response status is not 400");
let body = response.body;
client.assert(body.error === "can't make target with given coins", "Error message is incorrect");
})
%}
32 changes: 32 additions & 0 deletions change/Large target values.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### Large target values
# This test case checks if the API correctly returns the fewest number of coins for a target of 999 cents.
# The available coins are 1, 2, 5, 10, 20, 50, and 100 cents. The expected result is two 2 cent coins, one 5 cent coin, two 20 cent coins, one 50 cent coin, and seven 100 cent coins.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
2,
5,
10,
20,
50,
100
],
"target": 999
}

> {%
client.test("Large target values", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 15, "Number of coins is not 15");
client.assert(body.fewestCoins.filter(coin => coin === 2).length === 2, "The coins do not include two 2 cent coins");
client.assert(body.fewestCoins.filter(coin => coin === 5).length === 1, "The coins do not include one 5 cent coin");
client.assert(body.fewestCoins.filter(coin => coin === 20).length === 2, "The coins do not include two 20 cent coins");
client.assert(body.fewestCoins.filter(coin => coin === 50).length === 1, "The coins do not include one 50 cent coin");
client.assert(body.fewestCoins.filter(coin => coin === 100).length === 9, "The coins do not include nine 100 cent coins");
})
%}
27 changes: 27 additions & 0 deletions change/Multiple coin change.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### Multiple coin change
# This test case checks if the API correctly returns the fewest number of coins for a target of 15 cents.
# The available coins are 1, 5, 10, 25, and 100 cents. The expected result is one 5 cent coin and one 10 cent coin.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
5,
10,
25,
100
],
"target": 15
}

> {%
client.test("Multiple coin change", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 2, "Number of coins is not 2");
client.assert(body.fewestCoins.includes(5), "The coins do not include a 5 cent coin");
client.assert(body.fewestCoins.includes(10), "The coins do not include a 10 cent coin");
})
%}
25 changes: 25 additions & 0 deletions change/No coins make 0 change.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### No coins make 0 change
# This test case checks if the API correctly returns the fewest number of coins for a target of 0 cents.
# The available coins are 1, 5, 10, 21, and 25 cents. The expected result is an empty array.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
5,
10,
21,
25
],
"target": 0
}

> {%
client.test("No coins make 0 change", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 0, "Number of coins is not 0");
})
%}
28 changes: 28 additions & 0 deletions change/Possible change without unit coins available.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### Possible change without unit coins available
# This test case checks if the API correctly returns the fewest number of coins for a target of 21 cents.
# The available coins are 2, 5, 10, 20, and 50 cents. The expected result is three 2 cent coins, one 5 cent coin, and one 10 cent coin.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
2,
5,
10,
20,
50
],
"target": 21
}

> {%
client.test("Possible change without unit coins available", function () {
client.assert(response.status === 200, "Response status is not 200");
let body = response.body;
client.assert(body.fewestCoins.length === 5, "Number of coins is not 5");
client.assert(body.fewestCoins.filter(coin => coin === 2).length === 3, "The coins do not include three 2 cent coins");
client.assert(body.fewestCoins.includes(5), "The coins do not include a 5 cent coin");
client.assert(body.fewestCoins.includes(10), "The coins do not include a 10 cent coin");
})
%}
28 changes: 28 additions & 0 deletions change/Single coin change.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### Single coin change
# This test case checks if the API correctly returns the fewest number of coins for a target of 25 cents.
# The available coins are 1, 5, 10, 25, and 100 cents. The expected result is a single 25 cent coin.

POST {{host}}/api/change HTTP/1.1
Content-Type: application/json

{
"coins": [
1,
5,
10,
25,
100
],
"target": 25
}

> {%
client.test("Single coin change", function () {
client.assert(response.status === 200, "Response status is not 200");
const type = response.contentType.mimeType;
client.assert(type === "application/json", `Expected 'application/json' but received '${type}'`);
let body = response.body;
client.assert(body.fewestCoins.length === 1, "Number of coins is not 1");
client.assert(body.fewestCoins[0] === 25, "The coin is not 25 cents");
})
%}
Loading

0 comments on commit b4906f8

Please sign in to comment.