feat: solve high-score-board
Signed-off-by: Christina Sørensen <christina@cafkafk.com>
This commit is contained in:
parent
0a4ebc2eb6
commit
e82ff87a9f
9 changed files with 1317 additions and 0 deletions
20
jq/high-score-board/.exercism/config.json
Normal file
20
jq/high-score-board/.exercism/config.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"authors": [
|
||||
"glennj"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"high-score-board.jq"
|
||||
],
|
||||
"test": [
|
||||
"test-high-score-board.bats"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.jq"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"javascript/high-score-board"
|
||||
],
|
||||
"blurb": "Practice jq objects by tracking high scores of an arcade game."
|
||||
}
|
1
jq/high-score-board/.exercism/metadata.json
Normal file
1
jq/high-score-board/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"jq","exercise":"high-score-board","id":"7bfefebfdd8f43bfa13d4d30ba9c5f24","url":"https://exercism.org/tracks/jq/exercises/high-score-board","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/high-score-board/HELP.md
Normal file
114
jq/high-score-board/HELP.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
Each exercise contains a test file.
|
||||
Run the tests using the `bats` program.
|
||||
|
||||
```bash
|
||||
bats test-hello-world.bats
|
||||
```
|
||||
|
||||
`bats` will need to be installed.
|
||||
See the [Testing on the Bash track][bash] page for instructions to install `bats` for your system.
|
||||
|
||||
### bats is implemented in bash
|
||||
|
||||
The bats file is a bash script, with some special functions recognized by the `bats` command.
|
||||
You'll see some tests that look like
|
||||
|
||||
```sh
|
||||
jq -f some-exercise.jq <<< "{some,json,here}"
|
||||
```
|
||||
|
||||
That `<<<` syntax is a bash [Here String][here-string].
|
||||
It sends the string on the right-hand side into the standard input of the program on the left-hand side.
|
||||
It is ([approximately][so]) the same as
|
||||
|
||||
```sh
|
||||
echo "{some,json,here}" | jq -f some-exercise.jq
|
||||
```
|
||||
|
||||
## Help for assert functions
|
||||
|
||||
The tests use functions from the [bats-assert][bats-assert] library.
|
||||
Help for the various `assert*` functions can be found there.
|
||||
|
||||
## Skipped tests
|
||||
|
||||
Solving an exercise means making all its tests pass.
|
||||
By default, only one test (the first one) is executed when you run the tests.
|
||||
This is intentional, as it allows you to focus on just making that one test pass.
|
||||
Once it passes, you can enable the next test by commenting out or removing the
|
||||
|
||||
[[ $BATS_RUN_SKIPPED == true ]] || skip
|
||||
|
||||
annotations prepending other tests.
|
||||
|
||||
## Overriding skips
|
||||
|
||||
To run all tests, including the ones with `skip` annotations, you can run:
|
||||
|
||||
```bash
|
||||
BATS_RUN_SKIPPED=true bats test-some-exercise.bats
|
||||
```
|
||||
|
||||
It can be convenient to use a wrapper function to save on typing: in `bash` you can do:
|
||||
|
||||
```bash
|
||||
bats() {
|
||||
BATS_RUN_SKIPPED=true command bats *.bats
|
||||
}
|
||||
```
|
||||
|
||||
Then run tests with just:
|
||||
|
||||
```bash
|
||||
bats
|
||||
```
|
||||
|
||||
## Debugging in `jq`
|
||||
|
||||
`jq` comes with a handy [`debug`][debug] filter.
|
||||
Use it while you are developing your exercise solutions to inspect the data that is currently in the jq pipline.
|
||||
See the [debugging doc][debugging] for more details.
|
||||
|
||||
|
||||
[bash]: https://exercism.org/docs/tracks/bash/tests
|
||||
[bats-assert]: https://github.com/bats-core/bats-assert
|
||||
[here-string]: https://www.gnu.org/software/bash/manual/bash.html#Here-Strings
|
||||
[so]: https://unix.stackexchange.com/a/80372/4667
|
||||
[debug]: https://jqlang.github.io/jq/manual/v1.7/#debug
|
||||
[debugging]: https://exercism.org/docs/tracks/jq/debugging
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit high-score-board.jq` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [jq track's documentation](https://exercism.org/docs/tracks/jq)
|
||||
- The [jq track's programming category on the forum](https://forum.exercism.org/c/programming/jq)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
## Need more help?
|
||||
|
||||
- Go to the [Exercism Community forum](https://forum.exercism.org) to get support and ask questions (or just chat!)
|
||||
- Use the [Exercism Support](https://forum.exercism.org/c/support/8) category if you face any issues with working in the web editor, or downloading or submitting your exercises locally.
|
||||
- Use the [Programming:jq](https://forum.exercism.org/c/programming/jq/133) category for jq-specific topics.
|
||||
- Join the community on [Exercism's Discord server](https://exercism.org/r/discord).
|
||||
- [StackOverflow](https://stackoverflow.com/questions/tagged/jq) can be used to search for your problem and see if it has been answered already.
|
||||
You can also ask and answer questions.
|
||||
- [Github issue tracker](https://github.com/exercism/jq/issues) is where we track our development and maintainance of `jq` exercises in exercism.
|
||||
If none of the above links help you, feel free to post an issue here.
|
43
jq/high-score-board/HINTS.md
Normal file
43
jq/high-score-board/HINTS.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Hints
|
||||
|
||||
## 1. Create a new high score board
|
||||
|
||||
- Create a new object using curly braces.
|
||||
- Write the key as a string so the key can contain spaces.
|
||||
- Separate key and value using a colon.
|
||||
|
||||
## 2. Add players to a score board
|
||||
|
||||
- Use bracket notation to add a key with a name that is stored in a variable (the argument).
|
||||
- Use the assignment operator (`=`) to set a value for the new key.
|
||||
- Alternately, use the [`+` operator][man-plus] to merge the incoming object with a new one.
|
||||
- Remember that parentheses are needed around expressions for object keys.
|
||||
|
||||
## 3. Remove players from a score board
|
||||
|
||||
- Use the [del expression][man-del].
|
||||
- Reference the key like you have done in the task before (bracket notation).
|
||||
- Remember that the argument to `del` is a _index expression_ not just a string.
|
||||
|
||||
## 4. Increase a player's score
|
||||
|
||||
- First think about how to express the new value that you want to assign.
|
||||
- Then use the assignment operator like in task 2 to set that new value.
|
||||
- If you have not done so already, you can make use of the [arithmetic update-assignment operator][man-update-assignment] `+=`.
|
||||
|
||||
## 5. Apply Monday bonus points
|
||||
|
||||
- This would be a good place to use `to_entries`/`from_entries`, or `with_entries` to iterate over the object
|
||||
- For each key, set the new value as you did in task 4.
|
||||
|
||||
## 6. Find the total score
|
||||
|
||||
- We want to iterate over the _values_ of the object.
|
||||
We can use [`map_values`][man-map_values], or [`.[]`][man-brackets] to output a stream of values.
|
||||
- The `add` expression can be used to find the sum of a list of numbers.
|
||||
|
||||
[man-plus]: https://jqlang.github.io/jq/manual/v1.7/#addition
|
||||
[man-del]: https://jqlang.github.io/jq/manual/v1.7/#del
|
||||
[man-update-assignment]: https://jqlang.github.io/jq/manual/v1.7/#arithmetic-update-assignment
|
||||
[man-map_values]: https://jqlang.github.io/jq/manual/v1.7/#map-map_values
|
||||
[man-brackets]: https://jqlang.github.io/jq/manual/v1.7/#array-object-value-iterator
|
320
jq/high-score-board/README.md
Normal file
320
jq/high-score-board/README.md
Normal file
|
@ -0,0 +1,320 @@
|
|||
# High Score Board
|
||||
|
||||
Welcome to High Score Board on Exercism's jq Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Objects
|
||||
|
||||
A JSON **object** is, to use terminology from other languages, a "hash", "map", or "dictionary".
|
||||
|
||||
JSON defines an _object_ as:
|
||||
|
||||
> An object is an unordered set of **name**/**value** pairs.
|
||||
> An object begins with `{` left brace and ends with `}` right brace.
|
||||
> Each _name_ is followed by `:` colon and the _name/value_ pairs are separated by `,` comma.
|
||||
|
||||
The _name_ **must be a string**.
|
||||
Another word for _name_ is _key_.
|
||||
|
||||
The _value_ can be of any JSON type.
|
||||
Different _values_ in the same object can be of different types, like this example.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```json
|
||||
{
|
||||
"name": "Jane",
|
||||
"age": 42,
|
||||
"pets": ["cat", "fish"],
|
||||
"address": {"street": "123 Main St", "city": "Springfield"}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Note that there **must not** be a comma following the _last_ key-value pair.
|
||||
|
||||
### Creating objects
|
||||
|
||||
Use braces to collect the name/value pairs.
|
||||
Even though the names must be strings, they do not need to be quoted if the names are **identifier-like** (composed of alphanumeric characters and underscore, and not started with a digit).
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42}
|
||||
```
|
||||
|
||||
It is valid to use keys that are not _identifier-like_.
|
||||
Just quote them.
|
||||
|
||||
```jq
|
||||
{"first name": "Jane", "last name": "Lopez", age: 42}
|
||||
```
|
||||
|
||||
If the name is the result of an expression, the expression **must** be in parentheses.
|
||||
|
||||
```sh
|
||||
$ echo "Jane" | jq -Rc '{.: 42}'
|
||||
# verbose error message ...
|
||||
|
||||
$ echo "Jane" | jq -Rc '{(.): 42}'
|
||||
{"Jane":42}
|
||||
```
|
||||
|
||||
### Indexing
|
||||
|
||||
Values are retrieved from an object with **dot notation**.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | .age # => 42
|
||||
```
|
||||
|
||||
If you cannot refer to the key as an identifier, use **bracket notation**.
|
||||
|
||||
```jq
|
||||
"name" as $key | {name: "Jane", age: 42} | .$key # => error
|
||||
"name" as $key | {name: "Jane", age: 42} | .[$key] # => "Jane"
|
||||
```
|
||||
|
||||
### Adding or changing key-value pairs
|
||||
|
||||
To add a new key-value pair to an array, or to update the value of an existing key, use the `=` assignment operator, with an index expression on the left-hand side.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | .sport = "tennis" | .age = 21
|
||||
# => {
|
||||
# "name": "Jane",
|
||||
# "age": 21,
|
||||
# "sport": "tennis"
|
||||
# }
|
||||
```
|
||||
|
||||
The `+` operator will _merge_ objects.
|
||||
|
||||
```jq
|
||||
{Richard: 54} + {Jane: 42}
|
||||
# => {
|
||||
# "Richard": 54,
|
||||
# "Jane": 42
|
||||
# }
|
||||
```
|
||||
|
||||
### Removing a key
|
||||
|
||||
Use the `del` function to remove a key.
|
||||
It returns the updated object.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | del(.age) # => {"name": "Jane"}
|
||||
```
|
||||
|
||||
The parameter to `del` is an **index expression** (using dot- or bracket-notation) that resolves to a key in the object.
|
||||
`jq` calls it a **path expression**.
|
||||
It is not sufficient to just give a string.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | del(name) # error: name/0 is not defined
|
||||
{name: "Jane", age: 42} | del("name") # error: Invalid path expression with result "name"
|
||||
{name: "Jane", age: 42} | del(.name) # OK
|
||||
{name: "Jane", age: 42} | del(.["name"]) # OK
|
||||
```
|
||||
|
||||
### Membership
|
||||
|
||||
- To test if the object has a key, use the `has` function.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} as $example
|
||||
|
|
||||
($example | has("name")), # => true
|
||||
($example | has("sport")) # => false
|
||||
```
|
||||
|
||||
- Test if a key is in an object with `in`.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} as $example
|
||||
|
|
||||
("name" | in($example)), # => true
|
||||
("sport" | in($example)) # => false
|
||||
```
|
||||
|
||||
### List all the keys
|
||||
|
||||
Use the `keys` function to output a list of all the keys.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | keys # => ["age", "name"]
|
||||
```
|
||||
|
||||
Note that `keys` will _sort_ the keys.
|
||||
To retrieve the keys in the original order, use `keys_unsorted`.
|
||||
|
||||
There is no equivalent function to list all the _values_.
|
||||
However the `.[]` filter outputs the object values as a _stream_, and that stream can be captured with the `[...]` array constructor.
|
||||
|
||||
```jq
|
||||
[{first: "Jane", last: "Lopez", status: "awesome!"} | .[]]
|
||||
# => ["Jane", "Lopez", "awesome!"]
|
||||
```
|
||||
|
||||
### Iterating
|
||||
|
||||
- The `map_values(filter)` function applies the filter to each _value_ in the object.
|
||||
|
||||
```jq
|
||||
{first: "Jane", last: "Lopez", status: "awesome!"}
|
||||
| map_values(ascii_upcase)
|
||||
# => {"first": "JANE", "last": "LOPEZ", "status": "AWESOME!"}
|
||||
```
|
||||
|
||||
- To iterate over an object, we must first convert it to an array of key-value objects.
|
||||
The `to_entries` function does that.
|
||||
|
||||
```jq
|
||||
{name: "Jane", age: 42} | to_entries'
|
||||
# => [
|
||||
# {
|
||||
# "key": "name",
|
||||
# "value": "Jane"
|
||||
# },
|
||||
# {
|
||||
# "key": "age",
|
||||
# "value": 42
|
||||
# }
|
||||
# ]
|
||||
```
|
||||
|
||||
At this point, we can use array iteration functions, like `map`.
|
||||
|
||||
- The `from_entries` function is the inverse: convert an array of key-value objects into an object.
|
||||
|
||||
```jq
|
||||
[
|
||||
{"key":"name", "value":"Jane"},
|
||||
{"key":"age", "value":42}
|
||||
] | from_entries # =>{"name": "Jane", "age": 42}
|
||||
```
|
||||
|
||||
- To apply a filter to _each_ key-value pair in an object, use the `with_entries(filter)` function.
|
||||
|
||||
For example, given an object that maps a name to an age, the keys and values can be swapped as follows.
|
||||
|
||||
```jq
|
||||
{"Jane": 42, "Richard": 54}
|
||||
| with_entries({key: (.value | tostring), value: .key})
|
||||
```
|
||||
|
||||
outputs
|
||||
|
||||
```json
|
||||
{
|
||||
"42": "Jane",
|
||||
"54": "Richard"
|
||||
}
|
||||
```
|
||||
|
||||
`with_entries(filter)` is the same as
|
||||
|
||||
```jq
|
||||
to_entries | map(filter) | from_entries
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise, you are implementing a way to keep track of the high scores for the most popular game in your local arcade hall.
|
||||
|
||||
You have 6 functions to implement, mostly related to manipulating an object that holds high scores.
|
||||
|
||||
## 1. Create a new high score board
|
||||
|
||||
Write a function `create_score_board` which creates an object that serves as a high score board.
|
||||
The keys of this object will be the names of the players, the values will be their scores.
|
||||
For testing purposes, you want to directly include one entry in the object.
|
||||
This initial entry should consist of `"The Best Ever"` as player name and `1000000` as score.
|
||||
|
||||
```jq
|
||||
create_score_board
|
||||
# returns an object with one initial entry
|
||||
```
|
||||
|
||||
## 2. Add players to a score board
|
||||
|
||||
To add a player to the high score board, implement the function `add_player`.
|
||||
It takes a score board as input, and needs two parameters: the player name and the player's score.
|
||||
The function outputs the score board object with the new player added.
|
||||
|
||||
```jq
|
||||
{"José Valim", 486373}
|
||||
| add_player("Dave Thomas"; 0)
|
||||
# => {"Dave Thomas": 0, "José Valim": 486373} -- in some order
|
||||
```
|
||||
|
||||
## 3. Remove players from a score board
|
||||
|
||||
If players violate the rules of the arcade hall, they are manually removed from the high score board.
|
||||
Implement `remove_player` which takes a board as input and one parameter, the name of the player to remove.
|
||||
This function should remove the entry for the given player from the board and output the new board.
|
||||
If the player was not on the board in the first place, nothing should happen to the board; it should be returned as is.
|
||||
|
||||
```q
|
||||
{"Dave Thomas": 0} | remove_player("Dave Thomas")
|
||||
# => {}
|
||||
|
||||
{"Dave Thomas": 0} | remove_player("Rose Fanaras")
|
||||
# => {"Dave Thomas": 0}
|
||||
```
|
||||
|
||||
## 4. Increase a player's score
|
||||
|
||||
If a player finishes another game at the arcade hall, a certain amount of points will be added to the previous score on the board.
|
||||
Implement `update_score`, which takes a score board as input, and needs two parameters: the player name and the amount of score to add.
|
||||
The function should return the score board after the update was done.
|
||||
|
||||
```jq
|
||||
{"Freyja Ćirić": 12771000} | update_score("Freyja Ćirić"; 73)
|
||||
# => {"Freyja Ćirić": 12771073}
|
||||
```
|
||||
|
||||
## 5. Apply Monday bonus points
|
||||
|
||||
The arcade hall keeps a separate score board on Mondays.
|
||||
At the end of the day, each player on that board gets 100 additional points.
|
||||
|
||||
Implement the function `apply_monday_bonus`.
|
||||
The function adds the bonus points for each player that is listed on that board.
|
||||
|
||||
```jq
|
||||
{
|
||||
"Dave Thomas": 44,
|
||||
"Freyja Ćirić": 539,
|
||||
"José Valim": 265
|
||||
}
|
||||
| apply_monday_bonus
|
||||
# => {"Dave Thomas": 144, "Freyja Ćirić": 639, "José Valim": 365}
|
||||
```
|
||||
|
||||
## 6. Find the total score
|
||||
|
||||
Different arcade halls compete with each other to determine who has the best players.
|
||||
The arcade with the highest total score wins the honor.
|
||||
|
||||
Write a function `total_score`.
|
||||
It takes a score board as input, and outputs the sum of all the players' scores.
|
||||
|
||||
```jq
|
||||
{
|
||||
"Dave Thomas": 44,
|
||||
"Freyja Ćirić": 539,
|
||||
"José Valim": 265
|
||||
}
|
||||
| total_score
|
||||
# => 848
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @glennj
|
637
jq/high-score-board/bats-extra.bash
Normal file
637
jq/high-score-board/bats-extra.bash
Normal file
|
@ -0,0 +1,637 @@
|
|||
# This is the source code for bats-support and bats-assert, concatenated
|
||||
# * https://github.com/bats-core/bats-support
|
||||
# * https://github.com/bats-core/bats-assert
|
||||
#
|
||||
# Comments have been removed to save space. See the git repos for full source code.
|
||||
|
||||
############################################################
|
||||
#
|
||||
# bats-support - Supporting library for Bats test helpers
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
fail() {
|
||||
(( $# == 0 )) && batslib_err || batslib_err "$@"
|
||||
return 1
|
||||
}
|
||||
|
||||
batslib_is_caller() {
|
||||
local -i is_mode_direct=1
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-i|--indirect) is_mode_direct=0; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Arguments.
|
||||
local -r func="$1"
|
||||
|
||||
# Check call stack.
|
||||
if (( is_mode_direct )); then
|
||||
[[ $func == "${FUNCNAME[2]}" ]] && return 0
|
||||
else
|
||||
local -i depth
|
||||
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
|
||||
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
|
||||
done
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
batslib_err() {
|
||||
{ if (( $# > 0 )); then
|
||||
echo "$@"
|
||||
else
|
||||
cat -
|
||||
fi
|
||||
} >&2
|
||||
}
|
||||
|
||||
batslib_count_lines() {
|
||||
local -i n_lines=0
|
||||
local line
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
(( ++n_lines ))
|
||||
done < <(printf '%s' "$1")
|
||||
echo "$n_lines"
|
||||
}
|
||||
|
||||
batslib_is_single_line() {
|
||||
for string in "$@"; do
|
||||
(( $(batslib_count_lines "$string") > 1 )) && return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
batslib_get_max_single_line_key_width() {
|
||||
local -i max_len=-1
|
||||
while (( $# != 0 )); do
|
||||
local -i key_len="${#1}"
|
||||
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
|
||||
shift 2
|
||||
done
|
||||
echo "$max_len"
|
||||
}
|
||||
|
||||
batslib_print_kv_single() {
|
||||
local -ir col_width="$1"; shift
|
||||
while (( $# != 0 )); do
|
||||
printf '%-*s : %s\n' "$col_width" "$1" "$2"
|
||||
shift 2
|
||||
done
|
||||
}
|
||||
|
||||
batslib_print_kv_multi() {
|
||||
while (( $# != 0 )); do
|
||||
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
|
||||
printf '%s\n' "$2"
|
||||
shift 2
|
||||
done
|
||||
}
|
||||
|
||||
batslib_print_kv_single_or_multi() {
|
||||
local -ir width="$1"; shift
|
||||
local -a pairs=( "$@" )
|
||||
|
||||
local -a values=()
|
||||
local -i i
|
||||
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||
values+=( "${pairs[$i]}" )
|
||||
done
|
||||
|
||||
if batslib_is_single_line "${values[@]}"; then
|
||||
batslib_print_kv_single "$width" "${pairs[@]}"
|
||||
else
|
||||
local -i i
|
||||
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
|
||||
done
|
||||
batslib_print_kv_multi "${pairs[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
batslib_prefix() {
|
||||
local -r prefix="${1:- }"
|
||||
local line
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
printf '%s%s\n' "$prefix" "$line"
|
||||
done
|
||||
}
|
||||
|
||||
batslib_mark() {
|
||||
local -r symbol="$1"; shift
|
||||
# Sort line numbers.
|
||||
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
|
||||
|
||||
local line
|
||||
local -i idx=0
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
if (( ${1:--1} == idx )); then
|
||||
printf '%s\n' "${symbol}${line:${#symbol}}"
|
||||
shift
|
||||
else
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
(( ++idx ))
|
||||
done
|
||||
}
|
||||
|
||||
batslib_decorate() {
|
||||
echo
|
||||
echo "-- $1 --"
|
||||
cat -
|
||||
echo '--'
|
||||
echo
|
||||
}
|
||||
|
||||
############################################################
|
||||
|
||||
assert() {
|
||||
if ! "$@"; then
|
||||
batslib_print_kv_single 10 'expression' "$*" \
|
||||
| batslib_decorate 'assertion failed' \
|
||||
| fail
|
||||
fi
|
||||
}
|
||||
|
||||
assert_equal() {
|
||||
if [[ $1 != "$2" ]]; then
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'expected' "$2" \
|
||||
'actual' "$1" \
|
||||
| batslib_decorate 'values do not equal' \
|
||||
| fail
|
||||
fi
|
||||
}
|
||||
|
||||
assert_failure() {
|
||||
: "${output?}"
|
||||
: "${status?}"
|
||||
|
||||
(( $# > 0 )) && local -r expected="$1"
|
||||
if (( status == 0 )); then
|
||||
batslib_print_kv_single_or_multi 6 'output' "$output" \
|
||||
| batslib_decorate 'command succeeded, but it was expected to fail' \
|
||||
| fail
|
||||
elif (( $# > 0 )) && (( status != expected )); then
|
||||
{ local -ir width=8
|
||||
batslib_print_kv_single "$width" \
|
||||
'expected' "$expected" \
|
||||
'actual' "$status"
|
||||
batslib_print_kv_single_or_multi "$width" \
|
||||
'output' "$output"
|
||||
} \
|
||||
| batslib_decorate 'command failed as expected, but status differs' \
|
||||
| fail
|
||||
fi
|
||||
}
|
||||
|
||||
assert_line() {
|
||||
local -i is_match_line=0
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
: "${lines?}"
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-n|--index)
|
||||
if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
|
||||
echo "\`--index' requires an integer argument: \`$2'" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
is_match_line=1
|
||||
local -ri idx="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local -r expected="$1"
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$expected'" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_match_line )); then
|
||||
# Specific line.
|
||||
if (( is_mode_regexp )); then
|
||||
if ! [[ ${lines[$idx]} =~ $expected ]]; then
|
||||
batslib_print_kv_single 6 \
|
||||
'index' "$idx" \
|
||||
'regexp' "$expected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'regular expression does not match line' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ ${lines[$idx]} != *"$expected"* ]]; then
|
||||
batslib_print_kv_single 9 \
|
||||
'index' "$idx" \
|
||||
'substring' "$expected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line does not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ ${lines[$idx]} != "$expected" ]]; then
|
||||
batslib_print_kv_single 8 \
|
||||
'index' "$idx" \
|
||||
'expected' "$expected" \
|
||||
'actual' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line differs' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Contained in output.
|
||||
if (( is_mode_regexp )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} =~ $expected ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'regexp' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'no output line matches regular expression' \
|
||||
| fail
|
||||
elif (( is_mode_partial )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'substring' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'no output line contains substring' \
|
||||
| fail
|
||||
else
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} == "$expected" ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'line' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'output does not contain line' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
assert_output() {
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
local -i is_mode_nonempty=0
|
||||
local -i use_stdin=0
|
||||
: "${output?}"
|
||||
|
||||
# Handle options.
|
||||
if (( $# == 0 )); then
|
||||
is_mode_nonempty=1
|
||||
fi
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
-|--stdin) use_stdin=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: assert_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local expected
|
||||
if (( use_stdin )); then
|
||||
expected="$(cat -)"
|
||||
else
|
||||
expected="${1-}"
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_mode_nonempty )); then
|
||||
if [ -z "$output" ]; then
|
||||
echo 'expected non-empty output, but output was empty' \
|
||||
| batslib_decorate 'no output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_regexp )); then
|
||||
if [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$expected'" \
|
||||
| batslib_decorate 'ERROR: assert_output' \
|
||||
| fail
|
||||
elif ! [[ $output =~ $expected ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'regexp' "$expected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'regular expression does not match output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ $output != *"$expected"* ]]; then
|
||||
batslib_print_kv_single_or_multi 9 \
|
||||
'substring' "$expected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output does not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ $output != "$expected" ]]; then
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'expected' "$expected" \
|
||||
'actual' "$output" \
|
||||
| batslib_decorate 'output differs' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
assert_success() {
|
||||
: "${output?}"
|
||||
: "${status?}"
|
||||
|
||||
if (( status != 0 )); then
|
||||
{ local -ir width=6
|
||||
batslib_print_kv_single "$width" 'status' "$status"
|
||||
batslib_print_kv_single_or_multi "$width" 'output' "$output"
|
||||
} \
|
||||
| batslib_decorate 'command failed' \
|
||||
| fail
|
||||
fi
|
||||
}
|
||||
|
||||
refute() {
|
||||
if "$@"; then
|
||||
batslib_print_kv_single 10 'expression' "$*" \
|
||||
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
|
||||
| fail
|
||||
fi
|
||||
}
|
||||
|
||||
refute_line() {
|
||||
local -i is_match_line=0
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
: "${lines?}"
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-n|--index)
|
||||
if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
|
||||
echo "\`--index' requires an integer argument: \`$2'" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
is_match_line=1
|
||||
local -ri idx="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local -r unexpected="$1"
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_match_line )); then
|
||||
# Specific line.
|
||||
if (( is_mode_regexp )); then
|
||||
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||
batslib_print_kv_single 6 \
|
||||
'index' "$idx" \
|
||||
'regexp' "$unexpected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'regular expression should not match line' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||
batslib_print_kv_single 9 \
|
||||
'index' "$idx" \
|
||||
'substring' "$unexpected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line should not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||
batslib_print_kv_single 5 \
|
||||
'index' "$idx" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line should differ' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Line contained in output.
|
||||
if (( is_mode_regexp )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'no line should match the regular expression' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
elif (( is_mode_partial )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'no line should contain substring' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
else
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'line should not be in output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
refute_output() {
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
local -i is_mode_empty=0
|
||||
local -i use_stdin=0
|
||||
: "${output?}"
|
||||
|
||||
# Handle options.
|
||||
if (( $# == 0 )); then
|
||||
is_mode_empty=1
|
||||
fi
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
-|--stdin) use_stdin=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: refute_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local unexpected
|
||||
if (( use_stdin )); then
|
||||
unexpected="$(cat -)"
|
||||
else
|
||||
unexpected="${1-}"
|
||||
fi
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||
| batslib_decorate 'ERROR: refute_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_mode_empty )); then
|
||||
if [ -n "$output" ]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output non-empty, but expected no output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_regexp )); then
|
||||
if [[ $output =~ $unexpected ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'regexp' "$unexpected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'regular expression should not match output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ $output == *"$unexpected"* ]]; then
|
||||
batslib_print_kv_single_or_multi 9 \
|
||||
'substring' "$unexpected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output should not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ $output == "$unexpected" ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output equals, but it was expected to differ' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
29
jq/high-score-board/bats-jq.bash
Normal file
29
jq/high-score-board/bats-jq.bash
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# `bats-core` will consume both stdout and stderr for the `run` command's output.
|
||||
# However `jq` prints its DEBUG output on stderr.
|
||||
#
|
||||
# Lines starting with `["DEBUG:",` will be prefixed with a hash and printed on file descriptor 3.
|
||||
# Other lines on stderr will remain on stderr for bats to consume.
|
||||
#
|
||||
# See `bats-core` docs:
|
||||
# - "Printing to the terminal", https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal
|
||||
# - "File descriptor 3", https://bats-core.readthedocs.io/en/stable/writing-tests.html#file-descriptor-3-read-this-if-bats-hangs
|
||||
|
||||
|
||||
jq() {
|
||||
local output stderr rc line
|
||||
stderr=$(mktemp)
|
||||
output=$(command jq "$@" 2> "$stderr")
|
||||
rc=$?
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
if [[ $line == '["DEBUG:",'* ]]; then
|
||||
echo "# $line" >&3
|
||||
else
|
||||
echo "$line" >&2
|
||||
fi
|
||||
done < "$stderr"
|
||||
rm -f "$stderr"
|
||||
echo "$output"
|
||||
return "$rc"
|
||||
}
|
11
jq/high-score-board/high-score-board.jq
Normal file
11
jq/high-score-board/high-score-board.jq
Normal file
|
@ -0,0 +1,11 @@
|
|||
def create_score_board: {"The Best Ever": 1000000};
|
||||
|
||||
def add_player(player; score): . + {"\(player)": score};
|
||||
|
||||
def remove_player(player): .|del(.["\(player)"]);
|
||||
|
||||
def update_score(player; points): .["\(player)"] = (.["\(player)"] + points);
|
||||
|
||||
def apply_monday_bonus: .|map_values(. + 100);
|
||||
|
||||
def total_score: if . == {} then 0 else [.[]]|add end;
|
142
jq/high-score-board/test-high-score-board.bats
Normal file
142
jq/high-score-board/test-high-score-board.bats
Normal file
|
@ -0,0 +1,142 @@
|
|||
#!/usr/bin/env bats
|
||||
load bats-extra
|
||||
load bats-jq
|
||||
|
||||
assert_key_value() {
|
||||
local key=$1 expected=$2 actual
|
||||
actual=$(jq -rc --arg key "$key" '.[$key]' <<< "$output")
|
||||
assert_equal "$actual" "$expected"
|
||||
}
|
||||
|
||||
@test creates_a_new_board_with_a_test_entry {
|
||||
## task 1
|
||||
run jq -n -c '
|
||||
include "high-score-board";
|
||||
create_score_board
|
||||
'
|
||||
assert_success
|
||||
assert_output '{"The Best Ever":1000000}'
|
||||
}
|
||||
|
||||
@test adds_a_player_and_score_to_the_board {
|
||||
## task 2
|
||||
run jq '
|
||||
include "high-score-board";
|
||||
add_player("Jesse Johnson"; 1337)
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 99373,
|
||||
"Min-seo Shin": 0
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_key_value "Amil Pastorius" 99373 "$output"
|
||||
assert_key_value "Min-seo Shin" 0 "$output"
|
||||
assert_key_value "Jesse Johnson" 1337 "$output"
|
||||
}
|
||||
|
||||
@test removes_a_player_from_the_score_board {
|
||||
## task 3
|
||||
run jq -c '
|
||||
include "high-score-board";
|
||||
remove_player("Jesse Johnson")
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 99373,
|
||||
"Min-seo Shin": 0,
|
||||
"Jesse Johnson": 1337
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_output '{"Amil Pastorius":99373,"Min-seo Shin":0}'
|
||||
}
|
||||
|
||||
@test does_nothing_if_the_player_is_not_on_the_board {
|
||||
## task 3
|
||||
run jq -c '
|
||||
include "high-score-board";
|
||||
remove_player("Bruno Santangelo")
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 99373,
|
||||
"Min-seo Shin": 0,
|
||||
"Jesse Johnson": 1337
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_output '{"Amil Pastorius":99373,"Min-seo Shin":0,"Jesse Johnson":1337}'
|
||||
}
|
||||
|
||||
@test increases_a_players_score {
|
||||
## task 4
|
||||
run jq '
|
||||
include "high-score-board";
|
||||
.
|
||||
| update_score("Min-seo Shin"; 1999)
|
||||
| update_score("Jesse Johnson"; 1337)
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 99373,
|
||||
"Min-seo Shin": 0,
|
||||
"Jesse Johnson": 1337
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_key_value "Amil Pastorius" 99373 "$output"
|
||||
assert_key_value "Min-seo Shin" 1999 "$output"
|
||||
assert_key_value "Jesse Johnson" 2674 "$output"
|
||||
}
|
||||
|
||||
@test adds_100_points_for_all_players {
|
||||
## task 5
|
||||
run jq '
|
||||
include "high-score-board";
|
||||
apply_monday_bonus
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 345,
|
||||
"Min-seo Shin": 19,
|
||||
"Jesse Johnson": 122
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_key_value "Amil Pastorius" 445 "$output"
|
||||
assert_key_value "Min-seo Shin" 119 "$output"
|
||||
assert_key_value "Jesse Johnson" 222 "$output"
|
||||
}
|
||||
|
||||
@test does_nothing_if_the_score_board_is_empty {
|
||||
## task 5
|
||||
run jq -c '
|
||||
include "high-score-board";
|
||||
apply_monday_bonus
|
||||
' <<< '{}'
|
||||
assert_success
|
||||
assert_output '{}'
|
||||
}
|
||||
|
||||
@test total_score {
|
||||
## task 6
|
||||
run jq -c '
|
||||
include "high-score-board";
|
||||
total_score
|
||||
' << END_INPUT
|
||||
{
|
||||
"Amil Pastorius": 345,
|
||||
"Min-seo Shin": 19,
|
||||
"Jesse Johnson": 122
|
||||
}
|
||||
END_INPUT
|
||||
assert_success
|
||||
assert_output 486
|
||||
}
|
||||
|
||||
@test total_score_empty_board {
|
||||
## task 6
|
||||
run jq -c '
|
||||
include "high-score-board";
|
||||
total_score
|
||||
' <<< '{}'
|
||||
assert_success
|
||||
assert_output 0
|
||||
}
|
Loading…
Reference in a new issue