generated from coulomb/repo-seed
WP-0001-T001: service scaffold (Python, FastAPI, uv, ruff, mypy, pytest)
Lands the smallest credible foundation per ADR-0005:
- pyproject.toml: hatchling build, runtime deps (FastAPI, uvicorn, SQLAlchemy 2.0,
asyncpg, aiosqlite, alembic, blake3, cbor2, typer, structlog, pydantic,
pydantic-settings); dev deps (pytest, pytest-asyncio, httpx, hypothesis, ruff,
mypy); ruff + mypy --strict + pytest configured.
- uv.lock committed.
- Makefile thin shims: install / dev / test / lint / format / type / migrate / clean.
- src/artifactstore/ package skeleton with placeholder __init__.py per concern:
identity, manifest, events, retention, audit, storage, dataplane, registry,
api/http (minimal FastAPI app, GET / scaffold banner), cli (typer app with
version subcommand), config (pydantic-settings).
- tests/{unit,integration}/conftest.py present; unit smoke tests assert package
imports, HTTP root route, CLI version round-trip, settings defaults.
- .env.example documents ARTIFACTSTORE_DATABASE_URL,
ARTIFACTSTORE_STORAGE_LOCAL_ROOT, ARTIFACTSTORE_LOG_LEVEL.
- README updated with install / dev / test instructions.
- .gitignore: claude local state, local runtime data (var/, sqlite db).
make lint && make type && make test pass on a clean checkout (4 tests, 20
source files type-clean under mypy --strict).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
16
.env.example
Normal file
16
.env.example
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# artifact-store environment configuration
|
||||||
|
#
|
||||||
|
# Copy to .env for local development. All keys are prefixed with
|
||||||
|
# ARTIFACTSTORE_; pydantic-settings reads them automatically.
|
||||||
|
|
||||||
|
# Database connection string. SQLite (with aiosqlite) for dev; PostgreSQL
|
||||||
|
# (with asyncpg) for shared deployments.
|
||||||
|
# sqlite+aiosqlite:///./var/artifactstore.db
|
||||||
|
# postgresql+asyncpg://user:pass@host:5432/artifactstore
|
||||||
|
ARTIFACTSTORE_DATABASE_URL=sqlite+aiosqlite:///./var/artifactstore.db
|
||||||
|
|
||||||
|
# Local filesystem storage root. Used by the LocalBackend (WP-0001-T003).
|
||||||
|
ARTIFACTSTORE_STORAGE_LOCAL_ROOT=./var/storage
|
||||||
|
|
||||||
|
# Python logging level: DEBUG | INFO | WARNING | ERROR
|
||||||
|
ARTIFACTSTORE_LOG_LEVEL=INFO
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -174,3 +174,11 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# Claude Code local state (ralph loop, local permissions)
|
||||||
|
.claude/*.local.*
|
||||||
|
|
||||||
|
# artifact-store local runtime data
|
||||||
|
/var/
|
||||||
|
artifactstore.db
|
||||||
|
artifactstore.db-journal
|
||||||
|
|
||||||
|
|||||||
43
Makefile
Normal file
43
Makefile
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
.PHONY: help install dev test lint format type migrate clean
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "artifact-store — make targets"
|
||||||
|
@echo " install install / sync dependencies via uv"
|
||||||
|
@echo " dev run the FastAPI app with reload (uvicorn)"
|
||||||
|
@echo " test run the pytest suite"
|
||||||
|
@echo " lint ruff check + ruff format --check"
|
||||||
|
@echo " format ruff format (write changes)"
|
||||||
|
@echo " type mypy --strict over src and tests"
|
||||||
|
@echo " migrate alembic upgrade head (configured by WP-0001-T002)"
|
||||||
|
@echo " clean remove caches and build artefacts"
|
||||||
|
|
||||||
|
install:
|
||||||
|
uv sync --all-extras
|
||||||
|
|
||||||
|
dev:
|
||||||
|
uv run uvicorn artifactstore.api.http:app --reload --host 127.0.0.1 --port 8000
|
||||||
|
|
||||||
|
test:
|
||||||
|
uv run pytest
|
||||||
|
|
||||||
|
lint:
|
||||||
|
uv run ruff check .
|
||||||
|
uv run ruff format --check .
|
||||||
|
|
||||||
|
format:
|
||||||
|
uv run ruff format .
|
||||||
|
uv run ruff check --fix .
|
||||||
|
|
||||||
|
type:
|
||||||
|
uv run mypy
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
@if [ -f alembic.ini ]; then \
|
||||||
|
uv run alembic upgrade head; \
|
||||||
|
else \
|
||||||
|
echo "alembic.ini not present yet — see ARTIFACT-STORE-WP-0001-T002"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .pytest_cache .mypy_cache .ruff_cache build dist *.egg-info
|
||||||
|
find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
||||||
51
README.md
51
README.md
@@ -5,9 +5,8 @@ evidence packages, reports, logs, snapshots, exports, and release
|
|||||||
artifacts.
|
artifacts.
|
||||||
|
|
||||||
The registry owns artifact identity, metadata, provenance, retention
|
The registry owns artifact identity, metadata, provenance, retention
|
||||||
policy, and retrieval records. Bytes are delegated to configured
|
policy, and retrieval records. Bytes are delegated to configured storage
|
||||||
storage backends (local filesystem in v1, S3-compatible / Ceph RGW
|
backends (local filesystem in v1, S3-compatible / Ceph RGW next).
|
||||||
next).
|
|
||||||
|
|
||||||
The shape is library-first (`artifactstore` Python package); the HTTP
|
The shape is library-first (`artifactstore` Python package); the HTTP
|
||||||
server and the CLI are thin consumers. Content is addressed by digest;
|
server and the CLI are thin consumers. Content is addressed by digest;
|
||||||
@@ -16,26 +15,40 @@ are rebuildable.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Concept / service-baseline planning. No runnable scaffold yet —
|
Scaffold landed. The core kernels and local FS backend follow in the
|
||||||
`workplans/ARTIFACT-STORE-WP-0001-service-baseline.md` is the next step.
|
remaining tasks of [WP-0001](workplans/ARTIFACT-STORE-WP-0001-service-baseline.md).
|
||||||
|
|
||||||
## Start here
|
## Develop
|
||||||
|
|
||||||
|
Requires Python ≥ 3.12 and [`uv`](https://docs.astral.sh/uv/) on the path.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make install # uv sync --all-extras
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
make dev # uvicorn artifactstore.api.http:app --reload
|
||||||
|
make test # pytest
|
||||||
|
make lint # ruff check + ruff format --check
|
||||||
|
make type # mypy --strict
|
||||||
|
make migrate # alembic upgrade head (configured in WP-0001-T002)
|
||||||
|
```
|
||||||
|
|
||||||
|
The dev server listens on `127.0.0.1:8000`. The scaffold root route
|
||||||
|
returns `{"service": "artifact-store", "status": "scaffold"}`; the real
|
||||||
|
`/health` endpoint lands in `WP-0001-T014`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
- [INTENT.md](INTENT.md) — purpose, product thesis, scope, boundary.
|
- [INTENT.md](INTENT.md) — purpose, product thesis, scope, boundary.
|
||||||
- [SCOPE.md](SCOPE.md) — lightweight orientation.
|
- [SCOPE.md](SCOPE.md) — lightweight orientation.
|
||||||
- [docs/ARCHITECTURE-BLUEPRINT.md](docs/ARCHITECTURE-BLUEPRINT.md) — the
|
- [docs/ARCHITECTURE-BLUEPRINT.md](docs/ARCHITECTURE-BLUEPRINT.md) — v2
|
||||||
v2 architecture: modules, data model, API shape.
|
architecture: modules, data model, API shape.
|
||||||
- [docs/PLATFORM-AMBITION.md](docs/PLATFORM-AMBITION.md) — longer-horizon
|
- [docs/PLATFORM-AMBITION.md](docs/PLATFORM-AMBITION.md) — longer-horizon
|
||||||
thesis, ffmpeg / VLC reference points, the schema commitments v1
|
thesis and v1 schema commitments.
|
||||||
preserves.
|
- [docs/ROADMAP.md](docs/ROADMAP.md) — workplan sequencing across phases.
|
||||||
- [docs/ROADMAP.md](docs/ROADMAP.md) — workplan sequencing across
|
- [docs/adr/](docs/adr/) — architecture decision records.
|
||||||
phases.
|
|
||||||
- [docs/adr/](docs/adr/) — architecture decision records (ADR-0001 …
|
|
||||||
ADR-0006).
|
|
||||||
- [docs/ASSEMBLY-EXPERIMENT.md](docs/ASSEMBLY-EXPERIMENT.md) — opt-in
|
- [docs/ASSEMBLY-EXPERIMENT.md](docs/ASSEMBLY-EXPERIMENT.md) — opt-in
|
||||||
research line on hand-tuned assembly for hot kernels.
|
research line on hand-tuned asm for hot kernels.
|
||||||
- [docs/REVIEW-2026-05-15-intent-and-blueprint.md](docs/REVIEW-2026-05-15-intent-and-blueprint.md)
|
|
||||||
— the SWOT review that triggered this cleanup.
|
|
||||||
|
|
||||||
## Active workplans
|
## Active workplans
|
||||||
|
|
||||||
@@ -47,5 +60,5 @@ Concept / service-baseline planning. No runnable scaffold yet —
|
|||||||
|
|
||||||
## Agent operating notes
|
## Agent operating notes
|
||||||
|
|
||||||
See [AGENTS.md](AGENTS.md) for the StateHub-integrated session
|
See [AGENTS.md](AGENTS.md) for the StateHub-integrated session protocol,
|
||||||
protocol, workplan conventions, and progress-logging contract.
|
workplan conventions, and progress-logging contract.
|
||||||
|
|||||||
99
pyproject.toml
Normal file
99
pyproject.toml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling>=1.25"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "artifactstore"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Generic artifact registry and storage gateway"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
authors = [{ name = "artifact-store contributors" }]
|
||||||
|
keywords = ["artifact", "storage", "registry", "evidence", "retention"]
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 2 - Pre-Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Operating System :: POSIX :: Linux",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Topic :: Software Development :: Libraries",
|
||||||
|
"Topic :: System :: Archiving",
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"fastapi >= 0.115",
|
||||||
|
"uvicorn[standard] >= 0.30",
|
||||||
|
"sqlalchemy >= 2.0",
|
||||||
|
"asyncpg >= 0.29",
|
||||||
|
"aiosqlite >= 0.20",
|
||||||
|
"alembic >= 1.13",
|
||||||
|
"blake3 >= 0.4",
|
||||||
|
"cbor2 >= 5.6",
|
||||||
|
"typer >= 0.12",
|
||||||
|
"structlog >= 24.1",
|
||||||
|
"pydantic >= 2.7",
|
||||||
|
"pydantic-settings >= 2.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest >= 8.0",
|
||||||
|
"pytest-asyncio >= 0.23",
|
||||||
|
"httpx >= 0.27",
|
||||||
|
"hypothesis >= 6.100",
|
||||||
|
"ruff >= 0.6",
|
||||||
|
"mypy >= 1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
artifactstore = "artifactstore.cli:app"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/artifactstore"]
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest >= 8.0",
|
||||||
|
"pytest-asyncio >= 0.23",
|
||||||
|
"httpx >= 0.27",
|
||||||
|
"hypothesis >= 6.100",
|
||||||
|
"ruff >= 0.6",
|
||||||
|
"mypy >= 1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py312"
|
||||||
|
line-length = 100
|
||||||
|
src = ["src", "tests"]
|
||||||
|
extend-exclude = [".venv", "var", "migrations"]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "RET", "SIM", "RUF"]
|
||||||
|
ignore = [
|
||||||
|
"B008", # FastAPI dependency injection idiom
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"tests/**" = ["S101"]
|
||||||
|
"src/artifactstore/cli/__init__.py" = ["UP007"]
|
||||||
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
quote-style = "double"
|
||||||
|
indent-style = "space"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.12"
|
||||||
|
strict = true
|
||||||
|
files = ["src", "tests"]
|
||||||
|
mypy_path = "src"
|
||||||
|
explicit_package_bases = true
|
||||||
|
namespace_packages = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
testpaths = ["tests"]
|
||||||
|
pythonpath = ["src"]
|
||||||
|
addopts = "-q --strict-markers"
|
||||||
|
markers = [
|
||||||
|
"integration: marks tests as integration (requires DB / backend)",
|
||||||
|
]
|
||||||
8
src/artifactstore/__init__.py
Normal file
8
src/artifactstore/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
"""artifact-store: generic artifact registry and storage gateway.
|
||||||
|
|
||||||
|
The top-level package re-exports nothing yet. Submodules are documented in
|
||||||
|
``docs/ARCHITECTURE-BLUEPRINT.md`` and each carries its own placeholder for
|
||||||
|
the implementation tasks in workplan ARTIFACT-STORE-WP-0001.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
4
src/artifactstore/api/__init__.py
Normal file
4
src/artifactstore/api/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
"""HTTP and future RPC API surfaces.
|
||||||
|
|
||||||
|
Submodules: :mod:`artifactstore.api.http`.
|
||||||
|
"""
|
||||||
24
src/artifactstore/api/http/__init__.py
Normal file
24
src/artifactstore/api/http/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""FastAPI application entry point.
|
||||||
|
|
||||||
|
The full registry-aware ``/health`` endpoint lands in
|
||||||
|
ARTIFACT-STORE-WP-0001-T014. This scaffold exposes a minimal root route so
|
||||||
|
``make dev`` has something to serve while the registry layer is being built.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from artifactstore import __version__
|
||||||
|
|
||||||
|
app = FastAPI(title="artifact-store", version=__version__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root() -> dict[str, str]:
|
||||||
|
"""Return a service banner indicating the scaffold is up."""
|
||||||
|
return {
|
||||||
|
"service": "artifact-store",
|
||||||
|
"version": __version__,
|
||||||
|
"status": "scaffold",
|
||||||
|
}
|
||||||
5
src/artifactstore/audit/__init__.py
Normal file
5
src/artifactstore/audit/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Audit surface over the event log.
|
||||||
|
|
||||||
|
Audit is a view over :mod:`artifactstore.events` filtered to access and
|
||||||
|
lifecycle event types; it has no separate write path.
|
||||||
|
"""
|
||||||
36
src/artifactstore/cli/__init__.py
Normal file
36
src/artifactstore/cli/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"""artifact-store command-line interface.
|
||||||
|
|
||||||
|
The CLI is a thin consumer of :mod:`artifactstore.registry` (per ADR-0005).
|
||||||
|
The scaffold exposes only ``version``; richer subcommands land in later
|
||||||
|
tasks of ARTIFACT-STORE-WP-0001 and follow-on workplans.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import typer
|
||||||
|
|
||||||
|
from artifactstore import __version__
|
||||||
|
|
||||||
|
app = typer.Typer(
|
||||||
|
help="artifact-store: artifact registry and storage gateway",
|
||||||
|
no_args_is_help=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.callback()
|
||||||
|
def main() -> None:
|
||||||
|
"""Top-level CLI entry point.
|
||||||
|
|
||||||
|
Forces typer into multi-command mode so subcommands behave consistently
|
||||||
|
even while the scaffold only ships ``version``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def version() -> None:
|
||||||
|
"""Print the artifactstore version and exit."""
|
||||||
|
typer.echo(__version__)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
app()
|
||||||
30
src/artifactstore/config.py
Normal file
30
src/artifactstore/config.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"""Application configuration loaded from environment variables.
|
||||||
|
|
||||||
|
All settings are read from environment variables prefixed with
|
||||||
|
``ARTIFACTSTORE_``. A ``.env`` file at the repository root is honoured for
|
||||||
|
local development; see ``.env.example``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
"""Top-level service configuration."""
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_prefix="ARTIFACTSTORE_",
|
||||||
|
env_file=".env",
|
||||||
|
env_file_encoding="utf-8",
|
||||||
|
extra="ignore",
|
||||||
|
)
|
||||||
|
|
||||||
|
database_url: str = "sqlite+aiosqlite:///./var/artifactstore.db"
|
||||||
|
storage_local_root: str = "./var/storage"
|
||||||
|
log_level: str = "INFO"
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings() -> Settings:
|
||||||
|
"""Return a freshly-loaded :class:`Settings` instance."""
|
||||||
|
return Settings()
|
||||||
5
src/artifactstore/dataplane/__init__.py
Normal file
5
src/artifactstore/dataplane/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Data plane SPI and in-process implementation.
|
||||||
|
|
||||||
|
The SPI lands in ARTIFACT-STORE-WP-0001-T012. See ADR-0004 for the
|
||||||
|
control-plane / data-plane contract that this module isolates.
|
||||||
|
"""
|
||||||
5
src/artifactstore/events/__init__.py
Normal file
5
src/artifactstore/events/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Append-only event log and materialised-view replayer.
|
||||||
|
|
||||||
|
Real implementation lands in ARTIFACT-STORE-WP-0001-T011. See ADR-0002 for
|
||||||
|
the event-log-as-source-of-truth contract.
|
||||||
|
"""
|
||||||
5
src/artifactstore/identity/__init__.py
Normal file
5
src/artifactstore/identity/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Content addresses and digest abstraction.
|
||||||
|
|
||||||
|
Real implementation lands in ARTIFACT-STORE-WP-0001-T009. See ADR-0001 for
|
||||||
|
the dual-digest contract (BLAKE3 primary, SHA-256 retained for interop).
|
||||||
|
"""
|
||||||
5
src/artifactstore/manifest/__init__.py
Normal file
5
src/artifactstore/manifest/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Package manifest model and canonical-CBOR codec.
|
||||||
|
|
||||||
|
Real implementation lands in ARTIFACT-STORE-WP-0001-T010. See ADR-0003 for
|
||||||
|
the canonicalisation pin (RFC 8949 §4.2.2).
|
||||||
|
"""
|
||||||
6
src/artifactstore/registry/__init__.py
Normal file
6
src/artifactstore/registry/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
"""Registry orchestrator.
|
||||||
|
|
||||||
|
Real implementation lands in ARTIFACT-STORE-WP-0001-T013. The orchestrator
|
||||||
|
combines identity, manifest, events, retention, and dataplane into the
|
||||||
|
operations exposed by the HTTP API and CLI.
|
||||||
|
"""
|
||||||
5
src/artifactstore/retention/__init__.py
Normal file
5
src/artifactstore/retention/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Retention policy engine.
|
||||||
|
|
||||||
|
Seed classes land in ARTIFACT-STORE-WP-0001-T002 (data model). Active policy
|
||||||
|
operations (extensions, holds, sweeper) land in workplan WP-0003.
|
||||||
|
"""
|
||||||
5
src/artifactstore/storage/__init__.py
Normal file
5
src/artifactstore/storage/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Storage adapter SPI and backend registry.
|
||||||
|
|
||||||
|
The SPI and local filesystem backend land in ARTIFACT-STORE-WP-0001-T003.
|
||||||
|
The S3-compatible backend lands in workplan WP-0004.
|
||||||
|
"""
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
5
tests/conftest.py
Normal file
5
tests/conftest.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Top-level pytest configuration.
|
||||||
|
|
||||||
|
Shared fixtures land in this module as the test suite grows. The scaffold
|
||||||
|
keeps it empty so unit and integration suites configure themselves.
|
||||||
|
"""
|
||||||
0
tests/integration/__init__.py
Normal file
0
tests/integration/__init__.py
Normal file
5
tests/integration/conftest.py
Normal file
5
tests/integration/conftest.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Integration-test pytest configuration.
|
||||||
|
|
||||||
|
Integration suites require a database and storage backend; configuration
|
||||||
|
fixtures land here as those layers come online in later tasks.
|
||||||
|
"""
|
||||||
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
1
tests/unit/conftest.py
Normal file
1
tests/unit/conftest.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Unit-test pytest configuration."""
|
||||||
35
tests/unit/test_smoke.py
Normal file
35
tests/unit/test_smoke.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""Smoke tests asserting the scaffold imports cleanly."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
|
import artifactstore
|
||||||
|
from artifactstore.api.http import app as http_app
|
||||||
|
from artifactstore.cli import app as cli_app
|
||||||
|
from artifactstore.config import Settings, get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_version_exposed() -> None:
|
||||||
|
assert isinstance(artifactstore.__version__, str)
|
||||||
|
assert artifactstore.__version__
|
||||||
|
|
||||||
|
|
||||||
|
def test_http_app_root_route_registered() -> None:
|
||||||
|
routes = {getattr(r, "path", None) for r in http_app.routes}
|
||||||
|
assert "/" in routes
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_version_command_round_trips() -> None:
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(cli_app, ["version"])
|
||||||
|
assert result.exit_code == 0, result.output
|
||||||
|
assert artifactstore.__version__ in result.output
|
||||||
|
|
||||||
|
|
||||||
|
def test_settings_defaults_loadable() -> None:
|
||||||
|
settings = get_settings()
|
||||||
|
assert isinstance(settings, Settings)
|
||||||
|
assert settings.log_level
|
||||||
|
assert settings.database_url
|
||||||
|
assert settings.storage_local_root
|
||||||
@@ -66,7 +66,7 @@ cli (just `artifactstore version`, `artifactstore migrate`, `artifactstore repla
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ARTIFACT-STORE-WP-0001-T001
|
id: ARTIFACT-STORE-WP-0001-T001
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "84209430-ec3b-4c5e-924e-019c25434230"
|
state_hub_task_id: "84209430-ec3b-4c5e-924e-019c25434230"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user