team18‐iter1‐testing - snuhcs-course/swpp-2026-project-team-18 GitHub Wiki
JustInTime — Testing Documentation
1. Document Revision History
Version
Date
Changes
1.0
2026-10-08
Iteration 1: test strategy, suites, results on main8583f6f (0.10.0), acceptance-test traceability, defects with their regression tests, known gaps
Status: ✅ passed · 🚧 partial or not re-run · ❌ not started
2. Test Strategy
2.1 Test levels
Each level checks one artifact of the design, from the requirements down to single modules.
Level
Checks against
Our suites
Runs on
Size
Acceptance
Requirements (AT-n in the Requirements page)
End-to-end flows, demo checklist; mapped in section 5
Emulator, shared or local server
20 of 32 ATs pass
End-to-end (system)
User-visible scenarios
PublicSetupTest, DemoFlowTest driven by qa/demo/run.py
Emulator (instrumented)
2 flows, 18 steps
Integration
Interfaces between app, server and database
8 HTTP suites against a live local server, including the app–server contract check
Local server, throwaway SQLite
228 checks
Unit and in-process API
Modules and single endpoints
Backend pytest; app JUnit tests on the JVM
Process or JVM; no device, no network
795 + 528 tests
The suite follows the test pyramid: the 1,323 unit and in-process tests run in about 20 seconds,
the HTTP suites in under 30 seconds, and the two end-to-end flows in 5–7 minutes.
flowchart LR
dev["Developer"] -->|"push to main or pull request"| ci["GitHub Actions"]
ci --> be["Backend pytest<br/>795 tests"]
ci --> http["HTTP suites<br/>8 scripts, live local server"]
ci --> app["App unit tests, lint, debug build<br/>528 tests"]
dev -->|"on demand"| qa["qa/demo/run.py<br/>UI Automator on an emulator"]
qa --> local["Throwaway local server"]
qa --> shared["Shared server, new QA account"]
Loading
2.2 Types of testing
Type
Question
How we test it in Iteration 1
Status
Functional
Does it do what it should?
Unit and API tests per module and endpoint; end-to-end flows
✅
Acceptance
Does it meet the agreed requirements?
ATs mapped to tests (section 5); end-to-end flows; demo checklist
🚧 20 of 32 pass; the rest belong to later iterations
Security
Can it be misused?
test_security_audit.py (112 tests): another user's id returns 404, authentication and throttles on every view, no tracebacks; owner-scoped local stores (audit tests)
✅
Performance
Is it fast enough?
Not measured yet (N8–N11). Only timeout and cold-start behavior is tested
❌
Usability
Can users finish their tasks?
Heuristic evaluation is in Iteration 3; demo rehearsals only so far
❌
Compatibility
Does it work on supported devices?
Emulators on API 34 and API 36; a physical Galaxy phone only for installation (AT-24 not started)
Variances add, not standard deviations; the same input gives the same alarm; no probability without variance
Real responses
App–server contract
The app's DTO field names are compared with responses from a running server
Human judgement
End-to-end runs and demos
A screenshot and UI dump per step; the D01–D12 demo checklist
2.4 Failures and malformed input
Fault injection: tests simulate a failed Kakao route lookup (test_route_failure; the live
route reports degraded), a failed address search while place search still works
(test_address_failure_preserves_working_place_search), a token refresh that hits a 5xx or a
network error (RefreshOutcomeTest), server timeouts and 503s
(ColdStartRetryInterceptorTest), and an older server that omits new fields (null-safety
tests)
Backend:config/settings/test.py pins an in-memory SQLite database and blanks every
external API key, so client code takes its "no key" path. The Kakao and Seoul clients are
replaced with stubs through monkeypatch or mock.patch. Real external calls are checked by
hand with check_external_apis.py
App: no mocking library. Rules that would need a device are pulled into pure functions
and objects (SyncDecision, RouteRefreshDecision, TripGeofence, MorningSession) and
tested on the JVM. ColdStartRetryInterceptorTest puts a fake interceptor at the end of the
chain and counts calls. Audit tests read src/main to enforce rules that are hard to run
without a device: owner-scoped stores, init order, the token guard
Integration: each run uses a new SQLite file in the temp folder and a server on a free port
End-to-end: a separate QA app (com.swpp.wakeup.qa), a throwaway local server or a new
QA account on the shared server, and GPS fixes sent through adb along the Kakao route
2.6 Good-test practices (FIRST)
Evidence
Fast
795 backend tests in 12.5 s; 528 app tests in 8.6 s
Independent
Each backend test runs in a transaction that is rolled back afterwards; app tests build their own inputs
Repeatable
No network or real keys; time is passed in as a parameter (e.g., RouteRefreshDecision.dueEventIds(alarms, nowMillis)); Monte Carlo uses a fixed seed. CI runs on a clean machine without .env or local.properties
Self-validating
Every check asserts. The HTTP runner exits non-zero on any failure; an end-to-end run stops at the first failed step and saves failure.png
Timely
Fixes come with a regression test (section 6)
Tests are named after one behavior and its expected outcome, e.g.,
test_resend_does_not_overwrite or `POST 는 503 이어도 재시도하지 않는다` (a POST is not
retried on 503).
2.7 Automation, CI and review
CI job
What it runs
Fails on
Backend pytest
python -m pytest
Any failing test
App–server contract
scripts/run_local_suite.py: the 8 HTTP suites
Any failed check
Android
testDebugUnitTest lintDebug assembleDebug
A failing test, a lint error or a build error. Lint warnings are reported but don't block
CI runs on every push to main and every pull request, with no secrets. Checks that need a
Kakao key are skipped, not failed
57 runs since 2026-09-22: 53 passed, 2 failed, 2 cancelled. The failures were the first run
while CI was being set up (8c5af19), and 9a07472, where the contract check flagged a field
the app reads but the server doesn't send yet (route badges); the check was refined in
a21861d
In Iteration 1 most changes went to main directly, with CI on every push; one went through
a pull request (#1). The team plans to merge through pull requests with CI and review from
Iteration 2 (AI Collaboration Report, section 7)
Team members specified the behavior and chose the test scenarios; AI wrote parts of the test
code and QA scripts to those specifications (AI Collaboration Report, section 1)
3. Test Suites
3.1 Backend unit and API tests (pytest)
backend/apps/*/tests: 26 files, 795 tests. Pure modules (distributions, estimators) are tested
directly; endpoints through the Django test client on the in-memory database.
App
Tests
Covers
accounts
62
Sign-up (consent time, password rules, duplicates in any letter case, rollback when the profile fails), login, JWT refresh, home validation (inside Korea, both coordinates or neither), τ range, a new home recomputing events; the live password check uses the sign-up validators and never echoes the password
common
150
Security audit (112), settings (21), admin (9), health (8)
events
144
Alarm switch and the first-alarm-of-the-day rule (28), events (22), calendar import (26), place search and static map views (45), live route (23)
observations
32
Uploads; a resend is ignored and never overwrites; validation
planning
144
Distributions (49), estimators (35), the alarm computation (22), JSON defaults (14), prep scope (14), faster alternative (10)
Weekly report, lateness causes, calibration buckets and verdicts, isolation
routines
37
Blocks and preconditions, per-event selection (recomputes the alarm), block observations
routing
158
Place search with addresses first (56), real-time arrivals (38), route segments (36), route paths (15), alternatives (13)
3.2 Integration: HTTP suites against a live local server
backend/scripts/run_local_suite.py starts runserver, runs the scripts below in order and
stops. They catch what in-process tests miss: URL routing, middleware, real serialization and
throttles. The runner pins a throwaway SQLite file, because an emptied variable once sent 21 test
accounts to the shared database, and it refuses a busy port, because a leftover server once made
the suite test old code.
Script
Checks
check_auth_api.py
Sign-up, login, refresh and me over HTTP
check_events_api.py
A new account has no events; creating one lists it with its alarm plan
check_route_api.py
Route candidates → choose one → the alarm uses it → change it and recompute
check_origin_api.py
A chosen origin is saved on the event and used by the computation
check_observations_api.py
Resends add no rows; delay against the plan has the right sign
check_timezone.py
Times sent in KST are stored as UTC and the alarm arithmetic holds
check_routines_api.py
Routine endpoints through routing, middleware, serialization and throttles
check_app_contract.py
The app's DTO field names match real server responses
Run by hand, outside CI:
check_db_defaults.py: read-only check on the shared Postgres that columns added later have a
database default. A missing one once made writes fail with 500 while reads and health looked
normal, because migrations reach the database before the new code is deployed
Route progress, ETA, lateness outlook and arrival verdict, map camera and projection, morning session, routine blocks and the block form, weekly report view, calendar import
sensing
61
Departure and arrival rules (TripGeofenceTest, 44), trip state, trip notifications, background ownership of live-route calls
data/remote
65
DTO null safety under Gson, token-refresh outcomes, cold-start retry, server version check, place search parsing
data/local
32
Restoring files saved by older versions, cache age labels, owner rules and audits
background
19
When to re-register alarms and retry uploads (SyncDecisionTest), route refresh rules
ui/*
120
Screen state and formatting: sign-up gating, 12-hour time, route choice styles and timeline, map pick, place search, HomeViewModelinit order
data/repository
6
alarm_on mapping, relative day labels
(root)
1
Template test
3.4 End-to-end tests (instrumented)
Built by Junha Park. qa/demo/run.py builds the QA app, prepares the server and the emulator,
and runs two UI Automator tests on the real UI.
Test
Steps
PublicSetupTest (8)
Sign up · home by road-name address · prep time · two routine blocks · an event with a place-name destination and a walking route (date and time pickers, including AM/PM and midnight) · alarm reasoning and map · re-login keeps the data · alarm switch off and on
DemoFlowTest (10)
Log in · plan and map · arm the alarm and lock the screen · the real alarm rings · dismiss it and finish the prep blocks · depart and move · stay at the destination (the app's real 50 m / 120 s rule) · arrival shown after tracking stops · arrival shown after re-entering the screen · observations uploaded and counted in this week's report
Mode
Server
Needs
default
Throwaway local server
A Kakao key in backend/.env
--public-ui
Shared server, new QA account; setup steps only
Nothing
--public-e2e
Shared server, new QA account; both tests
Nothing
Each run saves result.json, server-evidence.json, a screenshot and UI dump per step, logs
and optional screen video
To keep a run to 5–7 minutes, the event's start time is adjusted through the API: before the
alarm, so it rings about 30 s after setup, and after the arrival, so the weekly report counts
the event. Prep blocks are declared as 1–2 minutes and marked done after 5 s each; the trip is
simulated in 65 s
Not covered: push notifications, recovery after reboot or offline periods, process restart,
real GPS. The runner works on macOS and Linux only
3.5 Manual checks
docs/demo-checklist.md: steps D01–D12 with pass criteria and a result template, for the
presentation device
Reboot re-registration (AT-6) was checked on the emulator: the alarm is restored from disk at
the same time (dumpsys alarm)
Physical device: a Galaxy S24 (Android 16) has 0.10.0 installed and launched. No functional
checks yet
4. Iteration 1 Results
Suite
Where
Date
Code
Result
Backend pytest
PM's laptop (Windows, Python 3.12.10)
2026-10-08
main8583f6f
✅ 795 passed in 12.5 s
App unit tests
PM's laptop (JDK 25 from Android Studio)
2026-10-08
8583f6f
✅ 528 passed, 0 skipped
HTTP suites
PM's laptop, external keys blanked as in CI
2026-10-08
8583f6f
✅ 8 of 8 scripts; 228 checks passed, 0 failed, 31 skipped (need a Kakao key)
Database defaults
Shared Postgres, read-only
2026-10-08
—
✅ 19 columns checked, none at risk
CI
GitHub Actions, run 37609461513
2026-10-07
8583f6f
✅ backend, contract, Android (tests, lint, build)
Local end-to-end
Junha Park, emulator API 34
2026-10-07
8583f6f
✅ 8 setup + 10 flow steps, twice in a row
Public setup
Junha Park
2026-10-05
0.9.0
✅ 7 steps in 65 s · 🚧 not re-run on 0.10.0
Public end-to-end
Junha Park
2026-10-05
0.9.0
✅ 7 + 10 steps in 342 s; arrival and report 1/1 · 🚧 not re-run on 0.10.0
Shared-server smoke
PM, emulator API 36
2026-10-08
Server 0.10.0
✅ Health reports 0.10.0. Sign-up, home by road-name address and prep time reach Home; road-name addresses come first in search, so the 10-07 search fix is live
HTTP suites in detail:
Script
Passed
Failed
Skipped
check_auth_api.py
22
0
0
check_events_api.py
21
0
4
check_route_api.py
16
0
18
check_origin_api.py
14
0
5
check_observations_api.py
28
0
0
check_timezone.py
✅ (prints no count)
0
—
check_routines_api.py
39
0
2
check_app_contract.py
88
0
2
The Iteration 1 flow (sign in → add an event → the server computes the alarm → it rings →
departure and arrival reach the server) passes end to end on the emulator against a local
server, and passed on the shared server at 0.9.0.
5. Acceptance Test Traceability
Each acceptance test from the Requirements page and the tests that check it. Test files are under
backend/apps/<app>/tests/ or APP/app/src/test/.
Writes failed with 500 while the database had a new column and the server still ran old code
New columns get a database default
check_db_defaults.py
CI, 2026-10-05
The contract check failed on a field the app reads but the server doesn't send yet
Optional fields that aren't implemented yet are reported separately (a21861d)
check_app_contract.py
End-to-end QA, 2026-10-07
Flaky steps: UI nodes went stale after the screen redrew, and minute taps on the clock rounded to 5 minutes
Every action finds its node again; the minute hand is dragged; stale retries are logged per step
Two full passes in a row
Open defects, without a test yet:
The arrival result is not restored after the app process restarts
When the tracking service restarts, the moving state and the live route are lost
Map panning can jump twice as far during consecutive gestures while a response is slow
A small pinch (12%) snaps a whole zoom level
7. AI Feature Evaluation
To be documented in Iteration 3, as the course schedules. The plan in the full specification:
Module
Metric
Target
Edge cases
Status
M1 prep time
MAE, quantile calibration
≤ 12 min global, ≤ 7 min personal after 2 weeks (N13)
No observations, all-nighter, slept elsewhere, cancelled event
Unit-tested; not evaluated on real data
M2 travel correction
MAE improvement over raw Kakao
≥ 20% (N14)
First trip on a route, weekend headways, heavy rain
Unit-tested; not evaluated on real data
M3 natural-language input
Place accuracy, tag macro-F1, hallucination rate
F1 ≥ 0.85, no invented places
Emoji or abbreviation-only titles, online events, mixed language
Not started
8. Known Gaps
Gap
Effect
What closes it
No physical-device run
N1 and N2 unverified; power saving and real GPS untested
AT-24: 7 mornings on a Galaxy phone
Public end-to-end not re-run on 0.10.0
The last shared-server pass is from 0.9.0
run.py --public-e2e on 0.10.0
The end-to-end runner fails on Windows (WinError 193)
Only macOS and Linux machines can run it
Windows support in run.py
No coverage measurement
We can't see which code no test reaches
Coverage reports in CI (Week 7)
Performance not measured
N8–N11 unknown; N10 is not met because a block toggle waits for the server
Timing on the shared server; battery measurement on a phone
No automated test for password masking in the app's HTTP log
N22 rests on code review
A unit test for the log redaction
QA accounts stay on the shared server and are not excluded from learning
QA trips could skew route corrections (N26)
Exclude QA accounts in training, or purge them after each run
9. How to Run
cd backend && python -m pytest # 795 tests; in-memory SQLite, no networkcd backend && python scripts/run_local_suite.py # 8 HTTP suites on a throwaway local servercd APP && ./gradlew testDebugUnitTest lintDebug assembleDebug # 528 tests, lint, build
backend/.venv/bin/python qa/demo/run.py --public-e2e --no-video # end to end on an emulator (macOS/Linux)
Use .venv\Scripts\python.exe and .\gradlew.bat on Windows. Setup for the end-to-end run is
in the iteration-1-demo README.