Skip to content

Commit 17faca3

Browse files
committed
feat(lab-01): Odoo Standalone -- PostgreSQL + Odoo 17 ERP, real compose and test script
1 parent 226b6b0 commit 17faca3

3 files changed

Lines changed: 133 additions & 102 deletions

File tree

.github/workflows/ci.yml

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -72,42 +72,27 @@ jobs:
7272
sarif_file: trivy-results.sarif
7373

7474
lab-01-smoke:
75-
name: Lab 01 — Smoke Test
75+
name: Lab 01 -- Odoo Standalone (PostgreSQL + ERP)
7676
runs-on: ubuntu-latest
7777
needs: validate
78-
continue-on-error: true # scaffold stubs; full lab runs on real VMs
78+
continue-on-error: true
7979
steps:
8080
- uses: actions/checkout@v4
81-
82-
- name: Generate CI env file
83-
run: |
84-
# Copy example env and inject CI-safe defaults for any unset port vars
85-
if [ -f .env.example ]; then cp .env.example .env; fi
86-
# Set port placeholder vars used in scaffold compose files
87-
echo "firstPort=389" >> .env
88-
echo "secondPort=9090" >> .env
89-
90-
- name: Validate standalone compose can start
91-
run: |
92-
docker compose -f docker/docker-compose.standalone.yml config --no-interpolate -q
93-
echo "Standalone compose structure is valid"
94-
81+
- name: Install tools
82+
run: sudo apt-get install -y curl
83+
- name: Validate standalone compose
84+
run: docker compose -f docker/docker-compose.standalone.yml config -q && echo "Standalone compose valid"
9585
- name: Start standalone stack
9686
run: docker compose -f docker/docker-compose.standalone.yml up -d
97-
98-
- name: Wait for health
99-
run: |
100-
echo "Waiting for services..."
101-
sleep 30
102-
docker compose -f docker/docker-compose.standalone.yml ps
103-
104-
- name: Run Lab 01 test script
105-
run: bash tests/labs/test-lab-01.sh
106-
87+
- name: Wait for PostgreSQL
88+
run: timeout 120 bash -c 'until docker exec odoo-s01-db pg_isready -U odoo > /dev/null 2>&1; do sleep 5; done'
89+
- name: Wait for Odoo
90+
run: timeout 240 bash -c 'until curl -sf http://localhost:8303/ | grep -qi odoo; do sleep 10; done'
91+
- name: Run Lab 13-01 test script
92+
run: bash tests/labs/test-lab-13-01.sh --no-cleanup
10793
- name: Collect logs on failure
10894
if: failure()
10995
run: docker compose -f docker/docker-compose.standalone.yml logs
110-
11196
- name: Cleanup
11297
if: always()
11398
run: docker compose -f docker/docker-compose.standalone.yml down -v
Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,57 @@
1-
# Lab 01 — Standalone: Complete Odoo ERP and business management in isolation
2-
# No external dependencies required.
1+
# Lab 01 — Standalone: Odoo ERP in complete isolation
2+
# Module 13 | No external dependencies required
33
---
44
services:
5-
odoo:
5+
odoo-s01-db:
6+
image: postgres:15-alpine
7+
container_name: odoo-s01-db
8+
restart: unless-stopped
9+
environment:
10+
POSTGRES_DB: postgres
11+
POSTGRES_USER: odoo
12+
POSTGRES_PASSWORD: OdooLab01!
13+
volumes:
14+
- odoo-s01-db-data:/var/lib/postgresql/data
15+
networks:
16+
- odoo-s01-net
17+
healthcheck:
18+
test: ["CMD-SHELL", "pg_isready -U odoo"]
19+
interval: 10s
20+
timeout: 5s
21+
retries: 5
22+
23+
odoo-s01-app:
624
image: odoo:17
7-
container_name: it-stack-odoo
25+
container_name: odoo-s01-app
826
restart: unless-stopped
9-
ports:
10-
- "8069:$firstPort"
27+
depends_on:
28+
odoo-s01-db:
29+
condition: service_healthy
1130
environment:
12-
- IT_STACK_ENV=lab-01-standalone
31+
HOST: odoo-s01-db
32+
PORT: "5432"
33+
USER: odoo
34+
PASSWORD: OdooLab01!
35+
command: --db-filter=odoo_lab01
36+
ports:
37+
- "8303:8069"
1338
volumes:
14-
- odoo_data:/var/lib/odoo
39+
- odoo-s01-data:/var/lib/odoo
40+
- odoo-s01-addons:/mnt/extra-addons
41+
networks:
42+
- odoo-s01-net
1543
healthcheck:
16-
test: ["CMD-SHELL", "curl -sf http://localhost/health || exit 1"]
17-
interval: 30s
44+
test: ["CMD-SHELL", "curl -sf http://localhost:8069/web/health | grep -q 'ok\\|pass\\|{' || curl -sf http://localhost:8069/ | grep -qi 'odoo\\|login\\|openerp' || exit 1"]
45+
interval: 20s
1846
timeout: 10s
19-
retries: 5
47+
retries: 10
2048
start_period: 60s
21-
networks:
22-
- it-stack-net
23-
24-
networks:
25-
it-stack-net:
26-
driver: bridge
2749

2850
volumes:
29-
odoo_data:
51+
odoo-s01-db-data:
52+
odoo-s01-data:
53+
odoo-s01-addons:
54+
55+
networks:
56+
odoo-s01-net:
57+
driver: bridge

tests/labs/test-lab-13-01.sh

Lines changed: 75 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,89 @@
11
#!/usr/bin/env bash
2-
# test-lab-13-01.sh — Lab 13-01: Standalone
3-
# Module 13: Odoo ERP and business management
4-
# Basic odoo functionality in complete isolation
2+
# test-lab-13-01.sh — Odoo Lab 01: Standalone
3+
# Module 13 | Lab 01 | Tests: basic Odoo ERP functionality in isolation
54
set -euo pipefail
65

7-
LAB_ID="13-01"
8-
LAB_NAME="Standalone"
9-
MODULE="odoo"
10-
COMPOSE_FILE="docker/docker-compose.standalone.yml"
11-
PASS=0
12-
FAIL=0
13-
14-
# ── Colors ────────────────────────────────────────────────────────────────────
15-
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
16-
CYAN='\033[0;36m'; NC='\033[0m'
17-
18-
pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS++)); }
19-
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)); }
20-
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
21-
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
22-
23-
echo -e "${CYAN}======================================${NC}"
24-
echo -e "${CYAN} Lab ${LAB_ID}: ${LAB_NAME}${NC}"
25-
echo -e "${CYAN} Module: ${MODULE}${NC}"
26-
echo -e "${CYAN}======================================${NC}"
27-
echo ""
6+
COMPOSE_FILE="$(dirname "$0")/../docker/docker-compose.standalone.yml"
7+
CLEANUP=true
8+
for arg in "$@"; do [[ "$arg" == "--no-cleanup" ]] && CLEANUP=false; done
9+
10+
WEB_PORT=8303
11+
DB_USER="odoo"
12+
DB_PASS="OdooLab01!"
13+
14+
PASS=0; FAIL=0
15+
pass() { echo "[PASS] $1"; ((PASS++)) || true; }
16+
fail() { echo "[FAIL] $1"; ((FAIL++)) || true; }
17+
section() { echo ""; echo "=== $1 ==="; }
18+
19+
cleanup() {
20+
if [[ "$CLEANUP" == "true" ]]; then
21+
echo "Cleaning up..."
22+
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans 2>/dev/null || true
23+
fi
24+
}
25+
trap cleanup EXIT
26+
27+
section "Starting Lab 01 Standalone Stack"
28+
docker compose -f "$COMPOSE_FILE" up -d
29+
echo "Waiting for PostgreSQL and Odoo to initialize..."
2830

29-
# ── 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
31+
section "PostgreSQL Health Check"
32+
for i in $(seq 1 30); do
33+
status=$(docker inspect odoo-s01-db --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
34+
[[ "$status" == "healthy" ]] && break; sleep 5
35+
done
36+
[[ "$(docker inspect odoo-s01-db --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "PostgreSQL healthy" || fail "PostgreSQL not healthy"
3437

35-
# ── PHASE 2: Health Checks ────────────────────────────────────────────────────
36-
info "Phase 2: Health Checks"
38+
section "Odoo App Health Check"
39+
for i in $(seq 1 60); do
40+
status=$(docker inspect odoo-s01-app --format '{{.State.Health.Status}}' 2>/dev/null || echo "waiting")
41+
[[ "$status" == "healthy" ]] && break
42+
echo " Waiting for Odoo ($i/60)..."
43+
sleep 10
44+
done
45+
[[ "$(docker inspect odoo-s01-app --format '{{.State.Health.Status}}')" == "healthy" ]] && pass "Odoo app healthy" || fail "Odoo app not healthy"
3746

38-
if docker compose -f "${COMPOSE_FILE}" ps | grep -q "running\|Up"; then
39-
pass "Container is running"
47+
section "Odoo Web UI"
48+
http_code=$(curl -so /dev/null -w "%{http_code}" -L "http://localhost:${WEB_PORT}/" 2>/dev/null || echo "000")
49+
[[ "$http_code" =~ ^(200|303|302)$ ]] && pass "Odoo web accessible (HTTP $http_code)" || fail "Odoo web returned HTTP $http_code"
50+
51+
curl -sf -L "http://localhost:${WEB_PORT}/" 2>/dev/null | grep -qi "odoo\|login\|openerp\|web client" && pass "Odoo web page content OK" || fail "Odoo web page content unexpected"
52+
53+
section "Odoo Health Endpoint"
54+
health_resp=$(curl -sf "http://localhost:${WEB_PORT}/web/health" 2>/dev/null || echo "")
55+
if echo "$health_resp" | grep -qiE '"status"\s*:\s*"pass"|"ok"|\{'; then
56+
pass "Odoo /web/health endpoint responding"
4057
else
41-
fail "Container is not running"
58+
# Older Odoo versions may not have /web/health — check Odoo root
59+
curl -sf "http://localhost:${WEB_PORT}/" | grep -qi "odoo" && pass "Odoo web accessible (health endpoint alt check)" || fail "Odoo health check failed"
4260
fi
4361

44-
# ── PHASE 3: Functional Tests ─────────────────────────────────────────────────
45-
info "Phase 3: Functional Tests (Lab 01 — Standalone)"
62+
section "Odoo JSON-RPC API"
63+
api_resp=$(curl -sf -X POST "http://localhost:${WEB_PORT}/web/dataset/call_kw" \
64+
-H "Content-Type: application/json" \
65+
-d '{"jsonrpc":"2.0","method":"call","id":1,"params":{"model":"res.lang","method":"search_read","args":[[]],"kwargs":{"fields":["name"],"limit":1}}}' \
66+
2>/dev/null || echo "")
67+
echo "$api_resp" | grep -q '"result"' && pass "Odoo JSON-RPC API responding" || {
68+
echo "$api_resp" | grep -q '"error"' && pass "Odoo JSON-RPC API responding (auth error expected)" || fail "Odoo JSON-RPC API not responding"
69+
}
4670

47-
# TODO: Add module-specific functional tests here
48-
# Example:
49-
# if curl -sf http://localhost:8069/health > /dev/null 2>&1; then
50-
# pass "Health endpoint responds"
51-
# else
52-
# fail "Health endpoint not reachable"
53-
# fi
71+
section "Database Connectivity"
72+
docker exec odoo-s01-db psql -U "${DB_USER}" -l 2>/dev/null | grep -q "odoo\|postgres" && pass "PostgreSQL accessible as odoo user" || fail "PostgreSQL connection failed"
5473

55-
warn "Functional tests for Lab 13-01 pending implementation"
74+
section "Container Configuration"
75+
restart_policy=$(docker inspect odoo-s01-app --format '{{.HostConfig.RestartPolicy.Name}}')
76+
[[ "$restart_policy" == "unless-stopped" ]] && pass "Restart policy: unless-stopped" || fail "Unexpected restart policy: $restart_policy"
5677

57-
# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58-
info "Phase 4: Cleanup"
59-
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60-
info "Cleanup complete"
78+
db_host=$(docker inspect odoo-s01-app --format '{{range .Config.Env}}{{println .}}{{end}}' | grep "^HOST=" | cut -d= -f2)
79+
[[ "$db_host" == "odoo-s01-db" ]] && pass "HOST env var set correctly" || fail "HOST env var not set (got: $db_host)"
6180

62-
# ── Results ───────────────────────────────────────────────────────────────────
63-
echo ""
64-
echo -e "${CYAN}======================================${NC}"
65-
echo -e " Lab ${LAB_ID} Complete"
66-
echo -e " ${GREEN}PASS: ${PASS}${NC} | ${RED}FAIL: ${FAIL}${NC}"
67-
echo -e "${CYAN}======================================${NC}"
81+
section "Named Volumes"
82+
docker volume ls | grep -q "odoo-s01-db-data" && pass "Volume odoo-s01-db-data exists" || fail "Volume odoo-s01-db-data missing"
83+
docker volume ls | grep -q "odoo-s01-data" && pass "Volume odoo-s01-data exists" || fail "Volume odoo-s01-data missing"
6884

69-
if [ "${FAIL}" -gt 0 ]; then
70-
exit 1
71-
fi
85+
echo ""
86+
echo "================================================"
87+
echo "Lab 01 Results: ${PASS} passed, ${FAIL} failed"
88+
echo "================================================"
89+
[[ $FAIL -eq 0 ]] && exit 0 || exit 1

0 commit comments

Comments
 (0)