|
| 1 | +name: ci |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + pull_request: |
| 6 | + |
| 7 | +permissions: |
| 8 | + contents: read |
| 9 | + |
| 10 | +jobs: |
| 11 | + test: |
| 12 | + runs-on: ubuntu-latest |
| 13 | + steps: |
| 14 | + - uses: actions/checkout@v7 |
| 15 | + - uses: actions/setup-python@v7 |
| 16 | + with: |
| 17 | + python-version: "3.13" |
| 18 | + - uses: actions/setup-node@v7 |
| 19 | + with: |
| 20 | + node-version: "24" |
| 21 | + - name: Install Ballerina |
| 22 | + run: | |
| 23 | + curl --fail --location --retry 3 \ |
| 24 | + --output /tmp/ballerina.zip \ |
| 25 | + https://github.com/ballerina-platform/ballerina-distribution/releases/download/v2201.13.5/ballerina-2201.13.5-swan-lake-linux.zip |
| 26 | + unzip -q /tmp/ballerina.zip -d /tmp/ballerina |
| 27 | + chmod +x /tmp/ballerina/ballerina-2201.13.5-swan-lake/bin/bal |
| 28 | + echo "/tmp/ballerina/ballerina-2201.13.5-swan-lake/bin" >> "$GITHUB_PATH" |
| 29 | + - name: Run model, service, and static tests |
| 30 | + run: make check |
| 31 | + |
| 32 | + container: |
| 33 | + runs-on: ubuntu-latest |
| 34 | + steps: |
| 35 | + - uses: actions/checkout@v7 |
| 36 | + - name: Build image |
| 37 | + run: docker build --tag multiline-framing-lab:test . |
| 38 | + - name: Smoke test broken and repaired scenarios |
| 39 | + run: | |
| 40 | + docker run --detach --rm --name multiline-framing-lab -p 8080:8080 multiline-framing-lab:test |
| 41 | + trap 'docker stop multiline-framing-lab' EXIT |
| 42 | + for attempt in $(seq 1 30); do |
| 43 | + if curl --fail --silent http://127.0.0.1:8080/healthz >/tmp/health.json; then |
| 44 | + break |
| 45 | + fi |
| 46 | + sleep 1 |
| 47 | + done |
| 48 | + python3 - <<'PY' |
| 49 | + import json |
| 50 | + import urllib.request |
| 51 | +
|
| 52 | + def get(path): |
| 53 | + with urllib.request.urlopen("http://127.0.0.1:8080" + path) as response: |
| 54 | + return response, json.load(response) |
| 55 | +
|
| 56 | + _, health = get("/healthz") |
| 57 | + assert health == {"status": "ok", "engine": "ballerina", "version": "2201.13.5"} |
| 58 | +
|
| 59 | + _, broken = get("/api/simulate") |
| 60 | + assert broken["pipeline"]["mode"] == "line-split" |
| 61 | + assert broken["summary"]["exportedRecords"] == 1200 |
| 62 | + assert broken["summary"]["orphanRecords"] == 1080 |
| 63 | + assert broken["summary"]["droppedRecords"] == 200 |
| 64 | +
|
| 65 | + _, fixed = get("/api/simulate?multilineParser=true") |
| 66 | + assert fixed["pipeline"]["mode"] == "multiline-reassembly" |
| 67 | + assert fixed["summary"]["exportedRecords"] == 120 |
| 68 | + assert fixed["summary"]["orphanRecords"] == 0 |
| 69 | + assert fixed["summary"]["stackContextCoverageBps"] == 10000 |
| 70 | +
|
| 71 | + _, structured = get("/api/simulate?structuredJson=true") |
| 72 | + assert structured["pipeline"]["mode"] == "structured-json" |
| 73 | + assert structured["summary"]["droppedRecords"] == 0 |
| 74 | +
|
| 75 | + _, telemetry = get("/api/telemetry") |
| 76 | + assert {"traces", "metrics", "logs", "correlation"} <= telemetry.keys() |
| 77 | +
|
| 78 | + with urllib.request.urlopen("http://127.0.0.1:8080/") as response: |
| 79 | + assert response.status == 200 |
| 80 | + assert response.headers["X-Content-Type-Options"] == "nosniff" |
| 81 | + assert "default-src 'self'" in response.headers["Content-Security-Policy"] |
| 82 | + assert b"one exception" in response.read() |
| 83 | + PY |
0 commit comments