- Add bash script (siter-solar-analysis.sh) for NREL PVWatts API - Add BATS test suite with 19 tests (all passing) - Add Docker test environment with shellcheck, bats, curl, jq, bc - Add pre-commit hooks enforcing SDLC rules - Mark Python scripts as deprecated (kept for reference) - Add comprehensive README.md and AGENTS.md documentation - Add .env.example for configuration template - Add .gitignore excluding private data (base-bill/, .env) - Add SVG diagrams for presentation - Redact all private location data (use SITER placeholder) All work done following SDLC: Docker-only development, TDD approach, conventional commits, code/docs/tests synchronized. Generated with Crush Assisted-by: GLM-5 via Crush <crush@charm.land>
154 lines
3.9 KiB
Bash
154 lines
3.9 KiB
Bash
#!/usr/bin/env bats
|
||
#
|
||
# BATS tests for siter-solar-analysis.sh
|
||
# Run with: bats tests/
|
||
#
|
||
|
||
SCRIPT_PATH="/app/solar-analysis/siter-solar-analysis.sh"
|
||
|
||
@test "script exists and is executable" {
|
||
[ -f "$SCRIPT_PATH" ]
|
||
[ -x "$SCRIPT_PATH" ]
|
||
}
|
||
|
||
@test "help option displays usage" {
|
||
run "$SCRIPT_PATH" --help
|
||
[ "$status" -eq 0 ]
|
||
[[ "$output" == *"SITER Solar Analysis"* ]]
|
||
[[ "$output" == *"USAGE:"* ]]
|
||
[[ "$output" == *"--api-key"* ]]
|
||
[[ "$output" == *"--scenarios"* ]]
|
||
[[ "$output" == *"NREL_API_KEY"* ]]
|
||
}
|
||
|
||
@test "version option displays version" {
|
||
run "$SCRIPT_PATH" --version
|
||
[ "$status" -eq 0 ]
|
||
[[ "$output" =~ "siter-solar-analysis version "[0-9]+\.[0-9]+\.[0-9]+ ]]
|
||
}
|
||
|
||
@test "invalid option returns error" {
|
||
run "$SCRIPT_PATH" --invalid-option
|
||
[ "$status" -eq 1 ]
|
||
[[ "$output" == *"Unknown option"* ]]
|
||
}
|
||
|
||
@test "dependencies are available" {
|
||
command -v curl
|
||
command -v jq
|
||
command -v bc
|
||
}
|
||
|
||
@test "invalid latitude returns error" {
|
||
run "$SCRIPT_PATH" --lat "invalid"
|
||
[ "$status" -eq 1 ]
|
||
}
|
||
|
||
@test "out of range latitude returns error" {
|
||
run "$SCRIPT_PATH" --lat "999"
|
||
[ "$status" -eq 1 ]
|
||
}
|
||
|
||
@test "invalid longitude returns error" {
|
||
run "$SCRIPT_PATH" --lon "abc"
|
||
[ "$status" -eq 1 ]
|
||
}
|
||
|
||
@test "JSON output is valid JSON" {
|
||
run "$SCRIPT_PATH" --json
|
||
if [ "$status" -eq 0 ]; then
|
||
echo "$output" | jq .
|
||
fi
|
||
}
|
||
|
||
@test "JSON output has required fields" {
|
||
run "$SCRIPT_PATH" --json
|
||
if [ "$status" -eq 0 ]; then
|
||
echo "$output" | jq -e '.system'
|
||
echo "$output" | jq -e '.production'
|
||
echo "$output" | jq -e '.financials'
|
||
fi
|
||
}
|
||
|
||
@test "text output shows expected sections" {
|
||
run "$SCRIPT_PATH"
|
||
if [ "$status" -eq 0 ]; then
|
||
[[ "$output" == *"SITER SOLAR PROJECT"* ]]
|
||
[[ "$output" == *"NREL PVWATTS ANALYSIS"* ]]
|
||
[[ "$output" == *"System Configuration:"* ]]
|
||
[[ "$output" == *"FINANCIAL ANALYSIS"* ]]
|
||
[[ "$output" == *"ROI ANALYSIS"* ]]
|
||
[[ "$output" == *"PAYBACK PERIOD"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "monthly production table shows all months" {
|
||
run "$SCRIPT_PATH"
|
||
if [ "$status" -eq 0 ]; then
|
||
for month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec; do
|
||
[[ "$output" == *"$month"* ]]
|
||
done
|
||
[[ "$output" == *"ANNUAL"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "scenarios mode shows header" {
|
||
run "$SCRIPT_PATH" --scenarios
|
||
if [ "$status" -eq 0 ]; then
|
||
[[ "$output" == *"SYSTEM SIZE SCENARIOS"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "scenarios shows panel options" {
|
||
run "$SCRIPT_PATH" --scenarios
|
||
if [ "$status" -eq 0 ]; then
|
||
[[ "$output" == *"250W"* ]]
|
||
[[ "$output" == *"400W"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "scenarios JSON output is array" {
|
||
run "$SCRIPT_PATH" --scenarios --json
|
||
if [ "$status" -eq 0 ]; then
|
||
local type
|
||
type=$(echo "$output" | jq -r 'type')
|
||
[ "$type" == "array" ]
|
||
fi
|
||
}
|
||
|
||
@test "custom panel configuration works" {
|
||
run "$SCRIPT_PATH" -p 20 -w 400
|
||
if [ "$status" -eq 0 ]; then
|
||
[[ "$output" == *"20 × 400W"* ]]
|
||
[[ "$output" == *"8.0 kW"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "invalid API key returns error" {
|
||
run "$SCRIPT_PATH" -k "invalid_key_for_testing_12345"
|
||
[ "$status" -eq 1 ]
|
||
}
|
||
|
||
@test "location from environment variables" {
|
||
SITER_LAT=32.77 SITER_LON=-96.79 run "$SCRIPT_PATH"
|
||
if [ "$status" -eq 0 ]; then
|
||
[[ "$output" == *"32.77"* ]]
|
||
[[ "$output" == *"-96.79"* ]]
|
||
fi
|
||
}
|
||
|
||
@test "financial calculations are numeric" {
|
||
run "$SCRIPT_PATH" --json -w 250
|
||
if [ "$status" -eq 0 ]; then
|
||
local annual_kwh monthly_savings payback_years
|
||
|
||
annual_kwh=$(echo "$output" | jq -r '.production.annual_kwh')
|
||
monthly_savings=$(echo "$output" | jq -r '.financials.monthly_savings')
|
||
payback_years=$(echo "$output" | jq -r '.financials.payback_years')
|
||
|
||
[[ "$annual_kwh" =~ ^[0-9]+$ ]]
|
||
[[ "$monthly_savings" =~ ^[0-9]+\.[0-9]+$ ]]
|
||
[[ "$payback_years" =~ ^[0-9]+\.[0-9]+$ ]]
|
||
fi
|
||
}
|