Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ FACTORY_SOURCE_MODE=external_demo_factory
FACTORY_CONNECTOR_MODE=read_only
NEXT_PUBLIC_API_BASE_URL=http://localhost:8000
FIP_API_URL=http://localhost:8000
FIP_PUBLIC_API_URL=http://localhost:8000
FIP_API_PORT=8000
FIP_WEB_PORT=3000
FACTORY_CONNECTION_PROFILES_STORE=.local/storage/connection_profiles.json
DEMO_FACTORY_HOST=host.docker.internal
DEMO_FACTORY_OPCUA_ENDPOINT=opc.tcp://host.docker.internal:4840/freeopcua/server/
Expand Down
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ OPCUA_POLL_COUNT ?= 6
OPCUA_POLL_INTERVAL ?= 1
OPCUA_EVENTS_STORE ?= .local/storage/opcua_demo_events.jsonl
COMPOSE_FILE ?= infra/docker/docker-compose.yml
COMPOSE_SMOKE_API_PORT ?= 18000
COMPOSE_SMOKE_WEB_PORT ?= 13000
COMPOSE_SMOKE_API_URL ?= http://localhost:$(COMPOSE_SMOKE_API_PORT)
COMPOSE_SMOKE_WEB_URL ?= http://localhost:$(COMPOSE_SMOKE_WEB_PORT)
PYTHONPATH := packages/factory-events:services/simulator:services/ingestion:services/process-sentinel:services/api
export PYTHONPATH

.PHONY: help setup dev dev-db compose-up compose-down compose-ps compose-logs compose-config simulate ingest opcua-demo-ingest sentinel-run demo demo-reset demo-data demo-ingest demo-sentinel-run demo-api-smoke api api-reload test test-unit test-integration test-contract test-e2e lint typecheck docs
.PHONY: help setup dev dev-db compose-up compose-down compose-ps compose-logs compose-config compose-smoke simulate ingest opcua-demo-ingest sentinel-run demo demo-reset demo-data demo-ingest demo-sentinel-run demo-api-smoke api api-reload test test-unit test-integration test-contract test-e2e lint typecheck docs

help:
@echo "Factory Intelligence Platform"
Expand All @@ -39,6 +43,7 @@ help:
@echo " make compose-ps List FIP Compose services"
@echo " make compose-logs Follow FIP Compose logs"
@echo " make compose-config Validate the FIP Compose file"
@echo " make compose-smoke Smoke test FIP Compose against Demo-Factory"
@echo " make simulate Generate simulator JSONL events"
@echo " make ingest Validate and ingest simulator events"
@echo " make opcua-demo-ingest Poll the local demo OPC UA server into FactoryEvents"
Expand Down Expand Up @@ -91,6 +96,9 @@ compose-logs:
compose-config:
docker compose -f $(COMPOSE_FILE) config

compose-smoke:
FIP_API_PORT=$(COMPOSE_SMOKE_API_PORT) FIP_WEB_PORT=$(COMPOSE_SMOKE_WEB_PORT) FIP_PUBLIC_API_URL=$(COMPOSE_SMOKE_API_URL) $(PYTHON) -m factory_api.compose_smoke --compose-file $(COMPOSE_FILE) --api-url $(COMPOSE_SMOKE_API_URL) --web-url $(COMPOSE_SMOKE_WEB_URL)

simulate:
$(PYTHON) -m factory_simulator.cli --scenario $(SCENARIO) --seed $(SEED) $(if $(DURATION_MINUTES),--duration-minutes $(DURATION_MINUTES),--count $(COUNT)) --output $(OUTPUT)

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ curl http://localhost:8000/health
make compose-down
```

Smoke test the composed runtime before opening Docker runtime PRs:

```bash
make compose-smoke
```

The smoke target uses FIP host ports `18000` and `13000` so it can run while
the full Demo-Factory stack owns ports `8000` and `3000`.

The wrappers call `docker compose -f infra/docker/docker-compose.yml ...`.
The equivalent direct commands are:

Expand Down Expand Up @@ -171,6 +180,7 @@ Run these before opening a pull request:
```bash
docker compose -f infra/docker/docker-compose.yml config
make compose-config
make compose-smoke
make lint
make typecheck
make test
Expand Down
11 changes: 11 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ curl http://localhost:8000/health
docker compose -f infra/docker/docker-compose.yml down
```

Before opening a runtime PR, use the composed smoke path to verify that the API
and Workbench render against the Docker stack:

```bash
make compose-smoke
```

The smoke target uses `http://localhost:18000` for the FIP API and
`http://localhost:13000` for the FIP Workbench so it can run beside the full
Demo-Factory stack.

The Workbench expects the API health endpoint to describe the active runtime
with `source_mode`, `storage_backend`, and `connector_mode`. In the Docker path,
that means external Demo-Factory/local protocol sources, Postgres-backed state,
Expand Down
50 changes: 50 additions & 0 deletions docs/LEARNING_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5058,3 +5058,53 @@ make typecheck
Run the full Compose smoke path from issue #241 and confirm the visible
Workbench recovery copy matches the real failure modes for Demo-Factory,
connector ingestion, Process Sentinel, API, and web startup.

## 2026-05-27 - Compose smoke test

### What changed

Added the issue #241 Compose smoke path. `make compose-smoke` now starts or
verifies the FIP Docker Compose stack, checks expected services, validates API
runtime metadata, confirms connector-ingested FactoryEvents, verifies readable
Process Sentinel state, and confirms the Workbench renders. The smoke command
uses FIP host ports `18000` and `13000` so it can run beside the full
Demo-Factory stack.

### Why it was built that way

The smoke path is a small Python helper under the existing API service package
so it can reuse the repo-local Python environment and Makefile conventions
without adding dependencies. It assumes Demo-Factory is already running as the
external local source fixture and keeps all checks read-only.

### How data flows through it

Demo-Factory protocol sources are consumed by the FIP read-only connector
worker through checked-in connection fixtures. Events are written to the
configured Postgres event store, Process Sentinel reads those events and writes
state, the API exposes health/events/detections, and the smoke helper verifies
the Workbench can render against the composed API.

### How to run it

```bash
cd ../Demo-Factory
docker compose up -d --build
cd ../Factory-Intelligence-Platform
make compose-smoke
```

### How to test it

```bash
.venv/bin/python -m pytest services/api/tests/test_compose_smoke.py
make test
make lint
make typecheck
```

### What to learn next

Run the smoke path with Demo-Factory intentionally stopped once and compare the
failure output against the runtime troubleshooting guide. Tighten any recovery
copy that does not lead directly to the missing source, service, or log output.
28 changes: 23 additions & 5 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,31 @@ make test-integration
make test-contract
make test-e2e
make demo-api-smoke
make compose-smoke
make lint
make typecheck
```

`make test-e2e` runs the local Operations Workbench Playwright smoke test. The
current test still exercises the direct local API and Workbench path while the
Dockerized runtime smoke path is being introduced under #241.
current test still exercises the direct local API and Workbench path. Use
`make compose-smoke` for the Dockerized Demo-Factory plus FIP runtime path.

The Playwright smoke test is local-only for now. CI integration is intentionally
tracked separately in #165 so this issue can keep the first browser workflow
reliable before hardening it for GitHub Actions.

`make demo-api-smoke` is a legacy backend-only smoke test for generated local
state. It is retained for compatibility while the Compose smoke path is being
introduced. Prefer the Docker/Demo-Factory runtime path for new documentation
and issue work.
state. It is retained for compatibility. Prefer the Docker/Demo-Factory runtime
path for new documentation and issue work.

`make compose-smoke` starts or verifies the FIP Compose stack after
Demo-Factory is already running separately. It checks expected services, API
runtime metadata, connector-ingested FactoryEvents, readable Process Sentinel
state, and Workbench rendering.

The smoke target uses `http://localhost:18000` for the FIP API and
`http://localhost:13000` for the FIP Workbench so it can run alongside the full
Demo-Factory stack, which may use ports `8000` and `3000`.

## Docker Compose Runtime Checks

Expand Down Expand Up @@ -115,6 +124,15 @@ curl http://localhost:8000/health
docker compose -f infra/docker/docker-compose.yml down
```

Run the composed smoke path before opening Docker runtime PRs:

```bash
make compose-smoke
```

If no FactoryEvents are visible through the API, the smoke command exits
non-zero with missing-source recovery guidance and Compose log snippets.

Use `docker compose down` for normal shutdown. Use
`docker compose -f infra/docker/docker-compose.yml down -v` only when a test or
manual reset intentionally deletes FIP named volumes.
Expand Down
58 changes: 54 additions & 4 deletions docs/runtime/DOCKER_COMPOSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ Demo-Factory protocols

The Dockerized runtime is being built across epic #232. The FIP Compose stack
now defines the API, Workbench, connector worker, Process Sentinel worker, and
Postgres service shape. Follow-up issues continue to harden the connector
runtime, scheduled Sentinel behavior, API/Workbench wording, and Compose smoke
tests.
Postgres service shape. `make compose-smoke` provides the current local
end-to-end smoke path for this composed runtime.

## Dependency Order

Expand Down Expand Up @@ -49,7 +48,9 @@ The default Compose services use explicit runtime variables:
| --- | --- | --- |
| `DATABASE_URL` | `postgresql://postgres:postgres@postgres:5432/factory_intelligence` | API, connector worker, Sentinel worker |
| `FACTORY_CONNECTION_PROFILES_STORE` | `.local/storage/connection_profiles.json` | API and connector profile loading |
| `NEXT_PUBLIC_API_BASE_URL` | `http://localhost:8000` | Workbench browser bundle |
| `FIP_API_PORT` | `8000` | Host port for the FIP API |
| `FIP_WEB_PORT` | `3000` | Host port for the FIP Workbench |
| `FIP_PUBLIC_API_URL` | `http://localhost:8000` | Workbench browser bundle API target |
| `FIP_API_URL` | `http://api:8000` | Container-to-container API target |
| `DEMO_FACTORY_HOST` | `host.docker.internal` | Connector worker source host note |
| `DEMO_FACTORY_OPCUA_ENDPOINT` | `opc.tcp://host.docker.internal:4840/freeopcua/server/` | Demo-Factory OPC-UA source endpoint |
Expand Down Expand Up @@ -138,6 +139,55 @@ make compose-config
make compose-down
```

## Compose Smoke Test

Before opening a PR that touches the Docker runtime, run the Compose smoke test
with Demo-Factory already running separately:

```bash
cd ../Demo-Factory
docker compose up -d --build

cd ../Factory-Intelligence-Platform
make compose-smoke
```

The smoke target uses host ports `18000` for the FIP API and `13000` for the
FIP Workbench so it can run while the full Demo-Factory stack owns ports `8000`
and `3000`. Override `COMPOSE_SMOKE_API_PORT` and `COMPOSE_SMOKE_WEB_PORT` if
those ports are busy.

The smoke command starts or verifies the FIP Compose stack with the checked-in
Demo-Factory connection profile fixtures, short connector/Sentinel polling
intervals, and Postgres-backed runtime state. It verifies:

- `docker compose -f infra/docker/docker-compose.yml ps` includes `postgres`,
`api`, `web`, `connector-worker`, and `sentinel-worker`.
- `GET http://localhost:18000/health` returns healthy runtime metadata,
including `source_mode`, `storage_backend`, and `connector_mode`.
- Connector ingestion has produced FactoryEvents through the composed API, and
`connector-worker` logs show emitted events during the smoke run.
- Process Sentinel detection state is readable. No detections is an acceptable
state when the external-source data has not crossed an advisory rule; inspect
`sentinel-worker` logs if detections were expected.
- The Workbench renders at `http://localhost:3000`.

If no FactoryEvents are available, or if the event store appears to contain
stale data without current connector emissions, the smoke command exits
non-zero with missing-source guidance and prints useful `docker compose ps`,
API, connector-worker, sentinel-worker, and web log output. Start with these
recovery commands:

```bash
cd ../Demo-Factory
docker compose ps

cd ../Factory-Intelligence-Platform
docker compose -f infra/docker/docker-compose.yml ps
docker compose -f infra/docker/docker-compose.yml logs --tail=100 connector-worker
curl http://localhost:18000/health
```

## Load Demo-Factory Connection Fixtures

Checked-in read-only Demo-Factory connection profile fixtures live in:
Expand Down
8 changes: 4 additions & 4 deletions infra/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
- --port
- "8000"
ports:
- "8000:8000"
- "${FIP_API_PORT:-8000}:8000"
healthcheck:
test:
[
Expand All @@ -61,15 +61,15 @@ services:
context: ../../apps/web
dockerfile: Dockerfile
args:
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-http://localhost:8000}
NEXT_PUBLIC_API_BASE_URL: ${FIP_PUBLIC_API_URL:-http://localhost:8000}
environment:
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-http://localhost:8000}
NEXT_PUBLIC_API_BASE_URL: ${FIP_PUBLIC_API_URL:-http://localhost:8000}
FIP_API_URL: http://api:8000
depends_on:
api:
condition: service_healthy
ports:
- "3000:3000"
- "${FIP_WEB_PORT:-3000}:3000"
healthcheck:
test:
[
Expand Down
Loading
Loading