@@ -26,33 +26,239 @@ echo -e "${CYAN} Module: ${MODULE}${NC}"
2626echo -e " ${CYAN} ======================================${NC} "
2727echo " "
2828
29+ # ── Helpers ───────────────────────────────────────────────────────────────────
30+ PG_HOST=localhost
31+ PG_PORT=5432
32+ ADMIN_USER=labadmin
33+ ADMIN_PASS=Lab01Password!
34+ APP_USER=appuser
35+ APP_PASS=AppPass123!
36+ TEST_USER=testuser
37+ TEST_PASS=TestPass123!
38+
39+ psql_admin () {
40+ PGPASSWORD=" ${ADMIN_PASS} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
41+ -U " ${ADMIN_USER} " -d labdb -t -c " $1 " 2> /dev/null
42+ }
43+
44+ psql_as () {
45+ local user=" $1 " pass=" $2 " db=" $3 " query=" $4 "
46+ PGPASSWORD=" ${pass} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
47+ -U " ${user} " -d " ${db} " -t -c " ${query} " 2> /dev/null
48+ }
49+
50+ psql_check () {
51+ local user=" $1 " pass=" $2 " db=" $3 " query=" $4 " test_name=" $5 "
52+ if PGPASSWORD=" ${pass} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
53+ -U " ${user} " -d " ${db} " -t -c " ${query} " > /dev/null 2>&1 ; then
54+ pass " ${test_name} "
55+ else
56+ fail " ${test_name} "
57+ fi
58+ }
59+
60+ wait_for_postgres () {
61+ local retries=30
62+ until PGPASSWORD=" ${ADMIN_PASS} " pg_isready -h " ${PG_HOST} " -p " ${PG_PORT} " \
63+ -U " ${ADMIN_USER} " > /dev/null 2>&1 ; do
64+ retries=$(( retries - 1 ))
65+ if [[ " ${retries} " -le 0 ]]; then
66+ fail " PostgreSQL did not become ready within 150 seconds"
67+ return 1
68+ fi
69+ info " Waiting for PostgreSQL... (${retries} retries left)"
70+ sleep 5
71+ done
72+ pass " PostgreSQL is ready"
73+ }
74+
2975# ── PHASE 1: Setup ────────────────────────────────────────────────────────────
30- info " Phase 1: Setup"
31- docker compose -f " ${COMPOSE_FILE} " up -d
32- info " Waiting 30s for ${MODULE} to initialize..."
33- sleep 30
76+ info " Phase 1: Setup"
77+ docker compose -f " ${COMPOSE_FILE} " pull --quiet 2> /dev/null || true
78+ docker compose -f " ${COMPOSE_FILE} " up -d --remove-orphans
79+ info " Waiting for PostgreSQL to be ready..."
80+ wait_for_postgres
3481
3582# ── PHASE 2: Health Checks ────────────────────────────────────────────────────
3683info " Phase 2: Health Checks"
3784
38- if docker compose -f " ${COMPOSE_FILE} " ps | grep -q " running\|Up" ; then
39- pass " Container is running"
85+ # Container running
86+ if docker compose -f " ${COMPOSE_FILE} " ps postgresql | grep -qE " running|Up|healthy" ; then
87+ pass " Container is running"
88+ else
89+ fail " Container is not running"
90+ fi
91+
92+ # Docker healthcheck status
93+ HEALTH=$( docker inspect --format=' {{.State.Health.Status}}' it-stack-postgresql-lab01 2> /dev/null)
94+ if [[ " ${HEALTH} " == " healthy" ]]; then
95+ pass " Docker healthcheck reports healthy"
96+ else
97+ warn " Docker healthcheck status: ${HEALTH} (may still be starting)"
98+ fi
99+
100+ # Port is open
101+ if nc -z -w3 " ${PG_HOST} " " ${PG_PORT} " 2> /dev/null; then
102+ pass " Port ${PG_PORT} is open"
40103else
41- fail " Container is not running"
104+ fail " Port ${PG_PORT} is not reachable"
105+ fi
106+
107+ # pg_isready
108+ if PGPASSWORD=" ${ADMIN_PASS} " pg_isready -h " ${PG_HOST} " -p " ${PG_PORT} " \
109+ -U " ${ADMIN_USER} " > /dev/null 2>&1 ; then
110+ pass " pg_isready confirms server accepting connections"
111+ else
112+ fail " pg_isready returned non-zero"
42113fi
43114
44115# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45- info " Phase 3: Functional Tests (Lab 01 — Standalone)"
116+ info " Phase 3: Functional Tests"
117+
118+ # 3.1 Admin connection
119+ info " 3.1 — Admin connection"
120+ psql_check " ${ADMIN_USER} " " ${ADMIN_PASS} " labdb " SELECT version();" \
121+ " Admin user can connect to labdb"
122+
123+ # 3.2 PostgreSQL version
124+ PG_VER=$( psql_admin " SELECT split_part(version(), ' ', 2);" | tr -d ' [:space:]' )
125+ if [[ " ${PG_VER} " == 16* ]]; then
126+ pass " PostgreSQL version is 16.x (got: ${PG_VER} )"
127+ else
128+ fail " Expected PostgreSQL 16.x, got: ${PG_VER} "
129+ fi
130+
131+ # 3.3 Databases exist
132+ info " 3.3 — Database existence"
133+ for db in labdb appdb testdb; do
134+ if psql_admin " SELECT 1 FROM pg_database WHERE datname='${db} ';" \
135+ | grep -q " 1" ; then
136+ pass " Database '${db} ' exists"
137+ else
138+ fail " Database '${db} ' not found"
139+ fi
140+ done
141+
142+ # 3.4 Users exist
143+ info " 3.4 — User existence"
144+ for u in appuser testuser; do
145+ if psql_admin " SELECT 1 FROM pg_roles WHERE rolname='${u} ';" \
146+ | grep -q " 1" ; then
147+ pass " User '${u} ' exists"
148+ else
149+ fail " User '${u} ' not found"
150+ fi
151+ done
152+
153+ # 3.5 User authentication
154+ info " 3.5 — User authentication"
155+ psql_check " ${APP_USER} " " ${APP_PASS} " appdb " SELECT current_user;" \
156+ " appuser authenticates to appdb"
157+ psql_check " ${TEST_USER} " " ${TEST_PASS} " testdb " SELECT current_user;" \
158+ " testuser authenticates to testdb"
159+
160+ # 3.6 Sample table and data from init script
161+ info " 3.6 — Init script data"
162+ if psql_admin " SELECT count(*) FROM it_stack_lab;" \
163+ | grep -qE " [0-9]+" ; then
164+ pass " it_stack_lab table exists and has rows"
165+ else
166+ fail " it_stack_lab table missing or empty (init script may have failed)"
167+ fi
168+
169+ # 3.7 CRUD operations
170+ info " 3.7 — CRUD operations"
171+ # INSERT
172+ psql_check " ${ADMIN_USER} " " ${ADMIN_PASS} " labdb \
173+ " INSERT INTO it_stack_lab (module, lab_number, status) VALUES ('test','00-00','created');" \
174+ " INSERT row into it_stack_lab"
175+
176+ # SELECT with filter
177+ if psql_admin " SELECT id FROM it_stack_lab WHERE status='created';" \
178+ | grep -qE " [0-9]+" ; then
179+ pass " SELECT with WHERE filter works"
180+ else
181+ fail " SELECT with WHERE filter returned no results"
182+ fi
183+
184+ # UPDATE
185+ psql_check " ${ADMIN_USER} " " ${ADMIN_PASS} " labdb \
186+ " UPDATE it_stack_lab SET status='updated' WHERE lab_number='00-00';" \
187+ " UPDATE row in it_stack_lab"
46188
47- # TODO: Add module-specific functional tests here
48- # Example:
49- # if curl -sf http://localhost:5432/health > /dev/null 2>&1; then
50- # pass "Health endpoint responds"
51- # else
52- # fail "Health endpoint not reachable"
53- # fi
189+ if psql_admin " SELECT 1 FROM it_stack_lab WHERE status='updated';" \
190+ | grep -q " 1" ; then
191+ pass " UPDATE confirmed via SELECT"
192+ else
193+ fail " UPDATE not reflected in SELECT"
194+ fi
195+
196+ # DELETE
197+ psql_check " ${ADMIN_USER} " " ${ADMIN_PASS} " labdb \
198+ " DELETE FROM it_stack_lab WHERE lab_number='00-00';" \
199+ " DELETE row from it_stack_lab"
200+
201+ # 3.8 Transaction test
202+ info " 3.8 — Transaction support"
203+ if PGPASSWORD=" ${ADMIN_PASS} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
204+ -U " ${ADMIN_USER} " -d labdb > /dev/null 2>&1 << 'SQL '
205+ BEGIN;
206+ INSERT INTO it_stack_lab (module, lab_number, status) VALUES ('txtest','00-99','txpending');
207+ UPDATE it_stack_lab SET status='txcommitted' WHERE lab_number='00-99';
208+ COMMIT;
209+ SQL
210+ pass " Transaction BEGIN/COMMIT succeeds"
211+ else
212+ fail " Transaction failed"
213+ fi
214+ psql_admin " DELETE FROM it_stack_lab WHERE lab_number='00-99';" > /dev/null 2>&1 || true
54215
55- warn " Functional tests for Lab 03-01 pending implementation"
216+ # Transaction ROLLBACK
217+ if PGPASSWORD=" ${ADMIN_PASS} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
218+ -U " ${ADMIN_USER} " -d labdb > /dev/null 2>&1 << 'SQL '
219+ BEGIN;
220+ INSERT INTO it_stack_lab (module, lab_number, status) VALUES ('rollbacktest','00-98','shouldnotexist');
221+ ROLLBACK;
222+ SQL
223+ if ! psql_admin " SELECT 1 FROM it_stack_lab WHERE lab_number='00-98';" \
224+ | grep -q " 1" ; then
225+ pass " ROLLBACK correctly discards changes"
226+ else
227+ fail " ROLLBACK failed — row persists after rollback"
228+ fi
229+ else
230+ fail " ROLLBACK test could not run"
231+ fi
232+
233+ # 3.9 Encoding and collation
234+ info " 3.9 — Encoding and collation"
235+ if psql_admin " SELECT encoding, datcollate FROM pg_database WHERE datname='labdb';" \
236+ | grep -q " UTF8" ; then
237+ pass " labdb uses UTF8 encoding"
238+ else
239+ fail " labdb encoding is not UTF8"
240+ fi
241+
242+ # 3.10 appuser privilege isolation (should NOT access testdb)
243+ info " 3.10 — Privilege isolation"
244+ if PGPASSWORD=" ${APP_PASS} " psql -h " ${PG_HOST} " -p " ${PG_PORT} " \
245+ -U " ${APP_USER} " -d testdb -c " SELECT 1;" > /dev/null 2>&1 ; then
246+ fail " appuser can connect to testdb (expected isolation failure)"
247+ else
248+ pass " appuser cannot connect to testdb (isolation correct)"
249+ fi
250+
251+ # 3.11 Performance baseline
252+ info " 3.11 — Performance baseline"
253+ START_MS=$( date +%s%3N)
254+ psql_admin " SELECT count(*) FROM generate_series(1, 100000);" > /dev/null 2>&1
255+ END_MS=$( date +%s%3N)
256+ ELAPSED=$(( END_MS - START_MS))
257+ if [[ " ${ELAPSED} " -lt 5000 ]]; then
258+ pass " generate_series(100000) completed in ${ELAPSED} ms (<5000ms threshold)"
259+ else
260+ warn " generate_series(100000) took ${ELAPSED} ms (may indicate slow I/O)"
261+ fi
56262
57263# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58264info " Phase 4: Cleanup"
0 commit comments