diff --git a/exercises/concept/intro-select/create_fixture.sql b/exercises/concept/intro-select/create_fixture.sql new file mode 100644 index 00000000..c96de796 --- /dev/null +++ b/exercises/concept/intro-select/create_fixture.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS weather_readings; +CREATE TABLE weather_readings ( + date TEXT NOT NULL, + location TEXT NOT NULL, + temperature INTEGER NOT NULL, + humidity INTEGER NOT NULL +); + +.mode csv +.import ./data.csv weather_readings diff --git a/exercises/concept/intro-select/data.csv b/exercises/concept/intro-select/data.csv new file mode 100644 index 00000000..cbe5e09a --- /dev/null +++ b/exercises/concept/intro-select/data.csv @@ -0,0 +1,6 @@ +"2025-10-22","Portland",53,72 +"2025-10-22","Seattle",56,66 +"2025-10-22","Boise",60,55 +"2025-10-23","Portland",54,70 +"2025-10-23","Seattle",57,68 +"2025-10-23","Boise",62,58 diff --git a/exercises/concept/intro-select/intro-select.sql b/exercises/concept/intro-select/intro-select.sql new file mode 100644 index 00000000..4c512429 --- /dev/null +++ b/exercises/concept/intro-select/intro-select.sql @@ -0,0 +1,33 @@ +CREATE TABLE all_data AS + -- Task 1. Select all records. + SELECT * FROM weather_readings +; + +CREATE TABLE location_and_temperature AS + -- Task 2. Select only location and temperature data. + -- Expect failure + SELECT temperature FROM weather_readings +; + +CREATE TABLE seattle AS + -- Task 3. Select all data for Seattle. + SELECT * FROM weather_readings WHERE location = 'Seattle' +; + +CREATE TABLE limited_humidity AS + -- Task 4. Select all data where the humidity is between 60% and 70%. + -- Expect failure + SELECT * FROM weather_readings WHERE humidity BETWEEN 50 AND 62 +; +.print "My debugging output:" +SELECT humidity from weather_readings; + +CREATE TABLE location AS + -- Task 5. Select only location data. + SELECT location FROM weather_readings +; + +CREATE TABLE unique_location AS + -- Task 5. Select only unique location data. + SELECT DISTINCT location FROM weather_readings +; diff --git a/exercises/concept/intro-select/intro-select_test.sql b/exercises/concept/intro-select/intro-select_test.sql new file mode 100644 index 00000000..478311cb --- /dev/null +++ b/exercises/concept/intro-select/intro-select_test.sql @@ -0,0 +1,17 @@ +-- Create database: +.read ./create_fixture.sql + +-- ASK: How can we correlate user output with specific tests? One way is to add .output statements +-- in the stub file. But that introduces more noise into the stub file. + +-- Run user solution and store results +.mode markdown +.output user_output.md +.read ./intro-select.sql +.shell rm -f ./results.db +.save ./results.db + +.output + +-- Report results +.shell bash ./report-results.sh results.db diff --git a/exercises/concept/intro-select/report-results.sh b/exercises/concept/intro-select/report-results.sh new file mode 100644 index 00000000..371a5f46 --- /dev/null +++ b/exercises/concept/intro-select/report-results.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +db_file=$1 +mapfile -t slugs < <(jq -r 'keys[]' test_data.json) + +# Generate result for each test +for slug in "${slugs[@]}"; do + actual=$(sqlite3 -json $db_file "SELECT * FROM ${slug};") + if [[ -z "$actual" ]]; then + actual="[]" + fi + jq -n --slurpfile test_data test_data.json --argjson got "${actual}" --arg slug "${slug}" -f test-result.jq +done > results.txt + +# Aggregate results +jq -n --slurpfile results results.txt '$results' > output.json diff --git a/exercises/concept/intro-select/test-result.jq b/exercises/concept/intro-select/test-result.jq new file mode 100644 index 00000000..4dbced99 --- /dev/null +++ b/exercises/concept/intro-select/test-result.jq @@ -0,0 +1,26 @@ +def columns: + if (. | length) == 0 then [] + else .[0] | keys + end; + +def rows: + map(to_entries | map(.value)); + +def failure_message(got; expected): + (got | columns | tostring) as $got_columns + | (expected | columns | tostring) as $expected_columns + | if $got_columns != $expected_columns then + "Expected columns \($expected_columns); but got \($got_columns)" + else + (got | rows | tostring) as $got_rows + | (expected | rows | tostring) as $expected_rows + | "With columns \($got_columns)\nexpected \($expected_rows)\nbut got \($got_rows)" + end; + +$test_data[0][$slug] +| del(.expected) as $entry +| if $got != .expected then + $entry + {"status": "fail", "message": failure_message($got; .expected)} + else + $entry + {"status": "pass"} + end diff --git a/exercises/concept/intro-select/test_data.json b/exercises/concept/intro-select/test_data.json new file mode 100644 index 00000000..e90689ec --- /dev/null +++ b/exercises/concept/intro-select/test_data.json @@ -0,0 +1,64 @@ +{ + "all_data": { + "task_id": 1, + "description": "All data", + "expected": [ + {"date": "2025-10-22", "location": "Portland", "temperature": 53, "humidity": 72}, + {"date": "2025-10-22", "location": "Seattle", "temperature": 56, "humidity": 66}, + {"date": "2025-10-22", "location": "Boise", "temperature": 60, "humidity": 55}, + {"date": "2025-10-23", "location": "Portland", "temperature": 54, "humidity": 70}, + {"date": "2025-10-23", "location": "Seattle", "temperature": 57, "humidity": 68}, + {"date": "2025-10-23", "location": "Boise", "temperature": 62, "humidity": 58} + ] + }, + "location_and_temperature": { + "task_id": 2, + "description": "Just location and temperature", + "expected": [ + {"location": "Portland", "temperature": 53}, + {"location": "Seattle", "temperature": 56}, + {"location": "Boise", "temperature": 60}, + {"location": "Portland", "temperature": 54}, + {"location": "Seattle", "temperature": 57}, + {"location": "Boise", "temperature": 62} + ] + }, + "seattle": { + "task_id": 3, + "description": "Seattle only", + "expected": [ + {"date": "2025-10-22", "location": "Seattle", "temperature": 56, "humidity": 66}, + {"date": "2025-10-23", "location": "Seattle", "temperature": 57, "humidity": 68} + ] + }, + "limited_humidity": { + "task_id": 4, + "description": "Humidity within range", + "expected": [ + {"date": "2025-10-22", "location": "Seattle", "temperature": 56, "humidity": 66}, + {"date": "2025-10-23", "location": "Portland", "temperature": 54, "humidity": 70}, + {"date": "2025-10-23", "location": "Seattle", "temperature": 57, "humidity": 68} + ] + }, + "location": { + "task_id": 5, + "description": "Just locations", + "expected": [ + {"location": "Portland"}, + {"location": "Seattle"}, + {"location": "Boise"}, + {"location": "Portland"}, + {"location": "Seattle"}, + {"location": "Boise"} + ] + }, + "unique_location": { + "task_id": 5, + "description": "Only unique locations", + "expected": [ + {"location": "Portland"}, + {"location": "Seattle"}, + {"location": "Boise"} + ] + } +}