Skip to content

feat(showcase): blog app — JSON CRUD (Phase 2)#522

Merged
ujeenet merged 2 commits into
developfrom
feat/e2e-showcase-phase2-blog
May 23, 2026
Merged

feat(showcase): blog app — JSON CRUD (Phase 2)#522
ujeenet merged 2 commits into
developfrom
feat/e2e-showcase-phase2-blog

Conversation

@ujeenet
Copy link
Copy Markdown
Owner

@ujeenet ujeenet commented May 23, 2026

Summary

Phase 2 of the E2E showcase plan. Adds the first sub-app: `blog`, a JSON CRUD surface over a single `Post` model. Exercises end-to-end:

  • `#[derive(Model)]` with `Auto` PK, `max_length`, `default = "false"`, `auto_now_add`.
  • `manage makemigrations` — `migrations/0002_blog.json` produced via the framework's own diff machinery (`cargo run -- makemigrations blog`).
  • Tri-dialect QuerySet pipeline: `Post::objects().order_by(...).filter_op(...).fetch_pool(&pool)`.
  • Macro-emitted `Post::insert_pool(&Pool)` round-trip with DB-populated `auto_now_add`.

Routes

Method Path Result
GET `/blog/posts` List, sorted by id ASC
GET `/blog/posts/{id}` Retrieve; 404 on miss
POST `/blog/posts` Create from `{title, body?, published?}`; 201 with body

Playwright

`e2e/tests/blog/crud.spec.ts` — 5 tests covering empty list, create + retrieve round-trip, ordering, 404 on unknown id, null body + default published. 7/7 tests pass locally (5 new + 2 smoke from phase 1).

Verified locally

```
$ npx playwright test
✓ blog API › list is initially empty (smoke against fresh DB) (9ms)
✓ blog API › POST creates a post + GET retrieves it by id (7ms)
✓ blog API › list reflects newly created posts (order_by id asc) (6ms)
✓ blog API › GET unknown id returns 404 (2ms)
✓ blog API › body is nullable (22ms)
✓ phase 1 smoke › info endpoint serves framework metadata (2ms)
✓ phase 1 smoke › backend matches the SHOWCASE_BACKEND env if set (1ms)
7 passed (4.2s)
```

CI matrix runs the same suite against postgres / mysql / sqlite.

ujeenet added 2 commits May 23, 2026 00:07
Adds the first sub-app under examples/showcase/. Exercises:

- #[derive(Model)] schema with Auto<i64> PK, max_length, default, and
  auto_now_add via the canonical macro surface.
- manage makemigrations — `cargo run -- makemigrations blog` produced
  migrations/0002_blog.json via the framework's diff machinery.
- Tri-dialect QuerySet pipeline:
  Post::objects().order_by(\&[(...)]).filter_op(col, op, val)
                .fetch_pool(\&pool)
- Macro-emitted Post::insert_pool(&Pool) round-trip with auto_now_add
  populated by the DB.

## Routes

GET  /blog/posts        — list, sorted by id ASC
GET  /blog/posts/{id}   — retrieve, 404 on miss
POST /blog/posts        — create from {title, body?, published?}

## Backend abstraction

The framework's non-tenancy runserver attaches different Extension
types per build (PgPool when feature=postgres, Pool otherwise).
`AttachedPool` + `into_pool()` hide the cfg fork so the handler
body works the same against every backend.

## Playwright

e2e/tests/blog/crud.spec.ts — 5 tests covering empty list, create +
retrieve round-trip, ordering, 404 on unknown id, null body +
default published. 7/7 tests pass locally (5 blog + 2 smoke from
phase 1).
The macro-emitted insert_pool populates Auto<T> PK on every backend
but auto_now_add fields only on dialects with INSERT ... RETURNING
(PG + SQLite). MySQL leaves created_at as Auto::Unset, which
serialized as an empty string and made the playwright assertion on
the RFC-3339 shape fail.

Re-fetch by PK after insert so the API contract is uniform across
the matrix.
@ujeenet ujeenet merged commit 7176466 into develop May 23, 2026
11 checks passed
@ujeenet ujeenet deleted the feat/e2e-showcase-phase2-blog branch May 23, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant