red64-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -58
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +2 -2
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/services/AgentInvoker.js +4 -4
- package/dist/services/AgentInvoker.js.map +1 -1
- package/dist/services/ClaudeHealthCheck.d.ts +5 -0
- package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
- package/dist/services/ClaudeHealthCheck.js +43 -5
- package/dist/services/ClaudeHealthCheck.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/framework/stacks/c/code-quality.md +326 -0
- package/framework/stacks/c/coding-style.md +347 -0
- package/framework/stacks/c/conventions.md +513 -0
- package/framework/stacks/c/error-handling.md +350 -0
- package/framework/stacks/c/feedback.md +158 -0
- package/framework/stacks/c/memory-safety.md +408 -0
- package/framework/stacks/c/tech.md +122 -0
- package/framework/stacks/c/testing.md +472 -0
- package/framework/stacks/cpp/code-quality.md +282 -0
- package/framework/stacks/cpp/coding-style.md +363 -0
- package/framework/stacks/cpp/conventions.md +420 -0
- package/framework/stacks/cpp/error-handling.md +264 -0
- package/framework/stacks/cpp/feedback.md +104 -0
- package/framework/stacks/cpp/memory-safety.md +351 -0
- package/framework/stacks/cpp/tech.md +160 -0
- package/framework/stacks/cpp/testing.md +323 -0
- package/framework/stacks/java/code-quality.md +357 -0
- package/framework/stacks/java/coding-style.md +400 -0
- package/framework/stacks/java/conventions.md +437 -0
- package/framework/stacks/java/error-handling.md +408 -0
- package/framework/stacks/java/feedback.md +180 -0
- package/framework/stacks/java/tech.md +126 -0
- package/framework/stacks/java/testing.md +485 -0
- package/framework/stacks/javascript/async-patterns.md +216 -0
- package/framework/stacks/javascript/code-quality.md +182 -0
- package/framework/stacks/javascript/coding-style.md +293 -0
- package/framework/stacks/javascript/conventions.md +268 -0
- package/framework/stacks/javascript/error-handling.md +216 -0
- package/framework/stacks/javascript/feedback.md +80 -0
- package/framework/stacks/javascript/tech.md +114 -0
- package/framework/stacks/javascript/testing.md +209 -0
- package/framework/stacks/loco/code-quality.md +156 -0
- package/framework/stacks/loco/coding-style.md +247 -0
- package/framework/stacks/loco/error-handling.md +225 -0
- package/framework/stacks/loco/feedback.md +35 -0
- package/framework/stacks/loco/loco.md +342 -0
- package/framework/stacks/loco/structure.md +193 -0
- package/framework/stacks/loco/tech.md +129 -0
- package/framework/stacks/loco/testing.md +211 -0
- package/framework/stacks/rust/code-quality.md +370 -0
- package/framework/stacks/rust/coding-style.md +475 -0
- package/framework/stacks/rust/conventions.md +430 -0
- package/framework/stacks/rust/error-handling.md +399 -0
- package/framework/stacks/rust/feedback.md +152 -0
- package/framework/stacks/rust/memory-safety.md +398 -0
- package/framework/stacks/rust/tech.md +121 -0
- package/framework/stacks/rust/testing.md +528 -0
- package/package.json +14 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Project Structure
|
|
2
|
+
|
|
3
|
+
## Organization Philosophy
|
|
4
|
+
|
|
5
|
+
Convention over configuration, inspired by Rails. File location implies behavior. Loco enforces a standard layout where folder structure, configuration shape, and wiring all matter. Extend with custom modules as complexity grows.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Directory Patterns
|
|
10
|
+
|
|
11
|
+
### Application Core (`src/`)
|
|
12
|
+
**Purpose**: All application code following Loco conventions
|
|
13
|
+
**Pattern**: Convention-driven -- generators create files in the right place
|
|
14
|
+
|
|
15
|
+
| Directory | Purpose | Naming |
|
|
16
|
+
|-----------|---------|--------|
|
|
17
|
+
| `controllers/` | HTTP request handlers (thin, delegate to models) | snake_case (`posts.rs`, `auth.rs`) |
|
|
18
|
+
| `models/` | Domain logic, ActiveRecord-style methods | snake_case (`users.rs`, `posts.rs`) |
|
|
19
|
+
| `models/_entities/` | Auto-generated SeaORM entities (read-only) | snake_case, prefixed with `_` |
|
|
20
|
+
| `views/` | JSON serialization structs and response shaping | snake_case (`posts.rs`, `auth.rs`) |
|
|
21
|
+
| `workers/` | Background job handlers | snake_case (`report_worker.rs`) |
|
|
22
|
+
| `tasks/` | CLI-invokable administrative tasks | snake_case (`sync_data.rs`) |
|
|
23
|
+
| `mailers/` | Email templates and delivery logic | snake_case (`auth.rs`) |
|
|
24
|
+
| `initializers/` | App startup hooks (extra DB, custom state) | snake_case (`view_engine.rs`) |
|
|
25
|
+
|
|
26
|
+
### Generated Entities (`src/models/_entities/`)
|
|
27
|
+
**Purpose**: SeaORM auto-generated entity files -- never edit manually
|
|
28
|
+
**Pattern**: Regenerated from database schema via `cargo loco db entities`
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/models/_entities/
|
|
32
|
+
mod.rs # Re-exports all entities
|
|
33
|
+
users.rs # Auto-generated User entity
|
|
34
|
+
posts.rs # Auto-generated Post entity
|
|
35
|
+
prelude.rs # Common imports
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Configuration (`config/`)
|
|
39
|
+
**Purpose**: Environment-specific YAML configuration
|
|
40
|
+
**Pattern**: One file per environment, merged with defaults
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
config/
|
|
44
|
+
development.yaml # Dev settings (auto_migrate: true, debug logging)
|
|
45
|
+
test.yaml # Test settings (dangerously_truncate: true)
|
|
46
|
+
production.yaml # Production settings (no dangerous flags)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Migrations (`migration/`)
|
|
50
|
+
**Purpose**: Database schema changes, versioned and reproducible
|
|
51
|
+
**Pattern**: Timestamped, descriptive names that drive auto-detection
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
migration/
|
|
55
|
+
src/
|
|
56
|
+
lib.rs # Migration registry
|
|
57
|
+
m20231001_000001_create_users.rs
|
|
58
|
+
m20231002_000001_create_posts.rs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Tests (`tests/`)
|
|
62
|
+
**Purpose**: Integration and request tests
|
|
63
|
+
**Pattern**: Mirror controller structure
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
tests/
|
|
67
|
+
requests/
|
|
68
|
+
posts.rs # Controller integration tests
|
|
69
|
+
auth.rs # Auth endpoint tests
|
|
70
|
+
models/
|
|
71
|
+
users.rs # Model unit tests
|
|
72
|
+
workers/
|
|
73
|
+
report_worker.rs # Worker tests
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Frontend (optional) (`frontend/`)
|
|
77
|
+
**Purpose**: SPA frontend when using Loco with a JS framework
|
|
78
|
+
**Pattern**: Separate directory, served as static assets
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Naming Conventions
|
|
83
|
+
|
|
84
|
+
### Rust / Loco
|
|
85
|
+
- **Models**: Singular, PascalCase for type (`User`, `Post`), snake_case for file (`users.rs`)
|
|
86
|
+
- **Controllers**: snake_case, grouped by resource (`posts.rs`, `auth.rs`)
|
|
87
|
+
- **Views**: snake_case, mirrors controller (`posts.rs`)
|
|
88
|
+
- **Workers**: Descriptive + Worker (`ReportWorker` in `report_worker.rs`)
|
|
89
|
+
- **Tasks**: Descriptive (`SyncData` in `sync_data.rs`)
|
|
90
|
+
- **Migrations**: Timestamped + descriptive (`m20231001_000001_create_users`)
|
|
91
|
+
- **Tables**: Plural, snake_case (`users`, `draft_posts`)
|
|
92
|
+
- **Columns**: snake_case (`created_at`, `user_id`)
|
|
93
|
+
|
|
94
|
+
### Files
|
|
95
|
+
- **Rust source**: snake_case (`user_service.rs`)
|
|
96
|
+
- **Config**: kebab or snake_case YAML (`development.yaml`)
|
|
97
|
+
- **Migrations**: Timestamp-prefixed (`m20231001_000001_*.rs`)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Module Organization
|
|
102
|
+
|
|
103
|
+
### `src/app.rs`
|
|
104
|
+
Central application hook -- register routes, workers, tasks, initializers:
|
|
105
|
+
|
|
106
|
+
```rust
|
|
107
|
+
impl Hooks for App {
|
|
108
|
+
fn routes(ctx: &AppContext) -> AppRoutes {
|
|
109
|
+
AppRoutes::with_default_routes()
|
|
110
|
+
.prefix("api")
|
|
111
|
+
.add_route(controllers::posts::routes())
|
|
112
|
+
.add_route(controllers::auth::routes())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
|
|
116
|
+
queue.register(ReportWorker::build(ctx)).await?;
|
|
117
|
+
Ok(())
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `src/lib.rs`
|
|
123
|
+
Module declarations and re-exports:
|
|
124
|
+
|
|
125
|
+
```rust
|
|
126
|
+
pub mod app;
|
|
127
|
+
pub mod controllers;
|
|
128
|
+
pub mod models;
|
|
129
|
+
pub mod views;
|
|
130
|
+
pub mod workers;
|
|
131
|
+
pub mod tasks;
|
|
132
|
+
pub mod mailers;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Configuration Patterns
|
|
138
|
+
|
|
139
|
+
### Environment-Specific (`config/*.yaml`)
|
|
140
|
+
- Development: `auto_migrate: true`, debug logging, relaxed settings
|
|
141
|
+
- Test: `dangerously_truncate: true` for clean test runs
|
|
142
|
+
- Production: no dangerous flags, explicit migration, secure JWT secret
|
|
143
|
+
|
|
144
|
+
### Key Config Sections
|
|
145
|
+
```yaml
|
|
146
|
+
server:
|
|
147
|
+
port: 5150
|
|
148
|
+
host: http://localhost
|
|
149
|
+
|
|
150
|
+
database:
|
|
151
|
+
uri: postgres://localhost/myapp_dev
|
|
152
|
+
auto_migrate: true
|
|
153
|
+
min_connections: 1
|
|
154
|
+
max_connections: 5
|
|
155
|
+
|
|
156
|
+
auth:
|
|
157
|
+
jwt:
|
|
158
|
+
secret: <secure-random>
|
|
159
|
+
expiration: 604800 # 7 days
|
|
160
|
+
|
|
161
|
+
workers:
|
|
162
|
+
mode: BackgroundAsync # or BackgroundQueue for Redis
|
|
163
|
+
|
|
164
|
+
queue:
|
|
165
|
+
kind: Redis
|
|
166
|
+
uri: redis://localhost:6379
|
|
167
|
+
|
|
168
|
+
logger:
|
|
169
|
+
level: debug
|
|
170
|
+
format: compact
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Import Organization
|
|
176
|
+
|
|
177
|
+
```rust
|
|
178
|
+
// Standard library
|
|
179
|
+
use std::sync::Arc;
|
|
180
|
+
|
|
181
|
+
// External crates
|
|
182
|
+
use loco_rs::prelude::*;
|
|
183
|
+
use sea_orm::*;
|
|
184
|
+
use serde::{Deserialize, Serialize};
|
|
185
|
+
|
|
186
|
+
// Internal modules
|
|
187
|
+
use crate::models::_entities::users;
|
|
188
|
+
use crate::views::posts::PostResponse;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
_Document patterns, not file trees. New files following patterns should not require updates._
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Technology Stack
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
Rust web application built on Loco framework (Rails-inspired). Axum-based HTTP layer, SeaORM for persistence, Tokio async runtime, background workers via Redis or in-process queues, single-binary deployment.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Technologies
|
|
10
|
+
|
|
11
|
+
- **Language**: Rust stable (Edition 2021, 1.70+)
|
|
12
|
+
- **Framework**: Loco (Rails-inspired, convention over configuration)
|
|
13
|
+
- **Web Server**: Axum (Tower-based, Tokio-native)
|
|
14
|
+
- **Async Runtime**: Tokio (multi-threaded)
|
|
15
|
+
- **ORM**: SeaORM (async, ActiveRecord-style)
|
|
16
|
+
- **Database**: PostgreSQL, SQLite, or MySQL
|
|
17
|
+
- **Serialization**: serde + serde_json
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Key Libraries
|
|
22
|
+
|
|
23
|
+
### Web & API
|
|
24
|
+
- **Axum**: HTTP routing and middleware (managed by Loco)
|
|
25
|
+
- **Tower / tower-http**: Middleware stack (CORS, compression, tracing, secure headers)
|
|
26
|
+
- **utoipa**: OpenAPI documentation generation
|
|
27
|
+
- **validator**: Struct validation with derive macros
|
|
28
|
+
|
|
29
|
+
### Database & Storage
|
|
30
|
+
- **SeaORM**: Async ORM with migration system and entity generation
|
|
31
|
+
- **sea-orm-cli**: Code generation and migration tooling
|
|
32
|
+
- **loco-rs storage**: Multi-backend storage (disk, S3, GCP, Azure, in-memory)
|
|
33
|
+
|
|
34
|
+
### Background Processing
|
|
35
|
+
- **sidekiq-rs** (via Loco): Redis-backed background job queue
|
|
36
|
+
- **Tokio tasks**: In-process async workers for lightweight jobs
|
|
37
|
+
|
|
38
|
+
### Authentication & Security
|
|
39
|
+
- **jsonwebtoken**: JWT token generation and validation
|
|
40
|
+
- **bcrypt**: Password hashing
|
|
41
|
+
- **loco-rs auth**: Built-in JWT + API key authentication middleware
|
|
42
|
+
|
|
43
|
+
### Serialization & Validation
|
|
44
|
+
- **serde**: Derive-based serialization/deserialization
|
|
45
|
+
- **serde_json**: JSON support
|
|
46
|
+
- **validator**: Struct-level validation with derive macros
|
|
47
|
+
|
|
48
|
+
### Observability
|
|
49
|
+
- **tracing**: Structured, async-aware logging and diagnostics
|
|
50
|
+
- **tracing-subscriber**: Log formatting (compact, pretty, JSON)
|
|
51
|
+
|
|
52
|
+
### Testing
|
|
53
|
+
- **cargo test / cargo-nextest**: Test execution
|
|
54
|
+
- **loco-rs testing**: Custom test kit per app layer (models, requests, workers)
|
|
55
|
+
- **insta**: Snapshot testing for views and responses
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Development Environment
|
|
60
|
+
|
|
61
|
+
### Required Tools
|
|
62
|
+
- Rust stable 1.70+ (Edition 2021)
|
|
63
|
+
- Cargo (bundled with rustup)
|
|
64
|
+
- Loco CLI (`cargo install loco`)
|
|
65
|
+
- SeaORM CLI (`cargo install sea-orm-cli`)
|
|
66
|
+
- PostgreSQL 14+ or SQLite3
|
|
67
|
+
- Redis 7+ (for queue-backed workers)
|
|
68
|
+
- Docker & Docker Compose (optional)
|
|
69
|
+
|
|
70
|
+
### Common Commands
|
|
71
|
+
```bash
|
|
72
|
+
# Project setup
|
|
73
|
+
loco new # Scaffold new project
|
|
74
|
+
cargo build # Build project
|
|
75
|
+
cargo build --release # Release build
|
|
76
|
+
|
|
77
|
+
# Dev server
|
|
78
|
+
cargo loco start # Start server
|
|
79
|
+
cargo loco start --server-and-worker # Server + workers in one process
|
|
80
|
+
|
|
81
|
+
# Workers
|
|
82
|
+
cargo loco start --worker # Standalone worker process
|
|
83
|
+
cargo loco start --worker email # Worker filtered by tag
|
|
84
|
+
|
|
85
|
+
# Generators
|
|
86
|
+
cargo loco generate model posts title:string! content:text
|
|
87
|
+
cargo loco generate controller posts
|
|
88
|
+
cargo loco generate scaffold posts title:string! content:text
|
|
89
|
+
cargo loco generate worker report_generator
|
|
90
|
+
cargo loco generate task sync_data
|
|
91
|
+
|
|
92
|
+
# Database
|
|
93
|
+
cargo loco db migrate # Run pending migrations
|
|
94
|
+
cargo loco db down # Rollback last migration
|
|
95
|
+
cargo loco db seed # Seed database
|
|
96
|
+
cargo loco db entities # Regenerate entities from schema
|
|
97
|
+
|
|
98
|
+
# Tasks
|
|
99
|
+
cargo loco task <task_name> # Run a task
|
|
100
|
+
|
|
101
|
+
# Code quality
|
|
102
|
+
cargo clippy -- -D warnings # Lint (deny warnings)
|
|
103
|
+
cargo fmt # Format
|
|
104
|
+
cargo test # All tests
|
|
105
|
+
cargo nextest run # Faster parallel test runner
|
|
106
|
+
|
|
107
|
+
# Deployment
|
|
108
|
+
cargo loco generate deployment # Generate Docker/Nginx/Shuttle configs
|
|
109
|
+
cargo loco doctor --production # Validate production environment
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Key Technical Decisions
|
|
115
|
+
|
|
116
|
+
| Decision | Rationale |
|
|
117
|
+
|----------|-----------|
|
|
118
|
+
| **Loco over bare Axum** | Convention over configuration; generators, auth, workers, and ORM integrated out of the box |
|
|
119
|
+
| **SeaORM over SQLx/Diesel** | ActiveRecord-style fits Loco's Rails philosophy; async-native, migration system, entity generation |
|
|
120
|
+
| **Axum (via Loco)** | Tower middleware ecosystem, Tokio-native, best DX in Rust web |
|
|
121
|
+
| **Redis queue over in-process** | Scales horizontally; workers run as separate processes in production |
|
|
122
|
+
| **JWT for authentication** | Stateless, SaaS-ready; built into Loco's starter templates |
|
|
123
|
+
| **tracing over log** | Structured, span-based, async-aware, OpenTelemetry compatible |
|
|
124
|
+
| **Single binary deployment** | No runtime dependencies on server; ~20MB binary, minimal resource usage |
|
|
125
|
+
| **Convention over configuration** | Folder structure, config shape, and wiring follow Loco conventions to reduce decision fatigue |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
_Document standards and patterns, not every dependency. See `loco.md` for detailed framework conventions._
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
Testing conventions for Loco applications. Loco provides a custom test kit tailored per app layer.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Test models thoroughly**: Fat models means model tests cover most logic
|
|
10
|
+
- **Test controllers via requests**: Integration tests over unit tests for HTTP layer
|
|
11
|
+
- **Test workers in isolation**: Use `ForegroundBlocking` mode in test config
|
|
12
|
+
- **Snapshot tests for views**: Reduce assertion boilerplate with `insta`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
### Enable Testing Features
|
|
19
|
+
```toml
|
|
20
|
+
# Cargo.toml
|
|
21
|
+
[dev-dependencies]
|
|
22
|
+
loco-rs = { version = "*", features = ["testing"] }
|
|
23
|
+
serial_test = "3"
|
|
24
|
+
insta = { version = "1", features = ["yaml", "redactions"] }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Test Configuration (`config/test.yaml`)
|
|
28
|
+
```yaml
|
|
29
|
+
database:
|
|
30
|
+
uri: postgres://localhost/myapp_test
|
|
31
|
+
dangerously_truncate: true # Clean DB per test
|
|
32
|
+
auto_migrate: true
|
|
33
|
+
|
|
34
|
+
workers:
|
|
35
|
+
mode: ForegroundBlocking # Synchronous worker execution
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Model Tests
|
|
41
|
+
|
|
42
|
+
Test domain logic directly on models:
|
|
43
|
+
|
|
44
|
+
```rust
|
|
45
|
+
#[tokio::test]
|
|
46
|
+
async fn test_create_user() {
|
|
47
|
+
let boot = boot_test::<App, Migrator>().await.unwrap();
|
|
48
|
+
seed::<App>(&boot.app_context).await.unwrap();
|
|
49
|
+
|
|
50
|
+
let user = users::ActiveModel::register(
|
|
51
|
+
&boot.app_context.db,
|
|
52
|
+
&RegisterParams {
|
|
53
|
+
email: "test@example.com".into(),
|
|
54
|
+
password: "secure123".into(),
|
|
55
|
+
name: "Test User".into(),
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
.await
|
|
59
|
+
.unwrap();
|
|
60
|
+
|
|
61
|
+
assert_eq!(user.email, "test@example.com");
|
|
62
|
+
assert!(user.email_verification_token.is_some());
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Request Tests (Controllers)
|
|
69
|
+
|
|
70
|
+
Generated automatically with `cargo loco generate controller`. Test full HTTP request/response cycle:
|
|
71
|
+
|
|
72
|
+
```rust
|
|
73
|
+
use loco_rs::testing::prelude::*;
|
|
74
|
+
|
|
75
|
+
#[tokio::test]
|
|
76
|
+
#[serial]
|
|
77
|
+
async fn test_list_posts() {
|
|
78
|
+
request::<App, Migrator, _, _>(|request, ctx| async move {
|
|
79
|
+
seed::<App>(&ctx).await.unwrap();
|
|
80
|
+
|
|
81
|
+
let response = request.get("/api/posts").await;
|
|
82
|
+
|
|
83
|
+
assert_eq!(response.status_code(), 200);
|
|
84
|
+
response.assert_json_contains(json!([{"title": "First Post"}]));
|
|
85
|
+
})
|
|
86
|
+
.await;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#[tokio::test]
|
|
90
|
+
#[serial]
|
|
91
|
+
async fn test_create_post_authenticated() {
|
|
92
|
+
request::<App, Migrator, _, _>(|request, ctx| async move {
|
|
93
|
+
let user = prepare_authenticated_user(&ctx).await;
|
|
94
|
+
|
|
95
|
+
let response = request
|
|
96
|
+
.post("/api/posts")
|
|
97
|
+
.add_header("Authorization", format!("Bearer {}", user.token))
|
|
98
|
+
.json(&json!({"title": "New Post", "content": "Hello"}))
|
|
99
|
+
.await;
|
|
100
|
+
|
|
101
|
+
assert_eq!(response.status_code(), 201);
|
|
102
|
+
})
|
|
103
|
+
.await;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Database Isolation
|
|
108
|
+
Use `request_with_create_db` for async tests requiring isolated schemas:
|
|
109
|
+
|
|
110
|
+
```rust
|
|
111
|
+
#[tokio::test]
|
|
112
|
+
async fn test_concurrent_safe() {
|
|
113
|
+
request_with_create_db::<App, Migrator, _, _>(|request, ctx| async move {
|
|
114
|
+
// Each test gets its own database schema
|
|
115
|
+
})
|
|
116
|
+
.await;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Worker Tests
|
|
123
|
+
|
|
124
|
+
Configure `ForegroundBlocking` in test config for synchronous execution:
|
|
125
|
+
|
|
126
|
+
```rust
|
|
127
|
+
#[tokio::test]
|
|
128
|
+
async fn test_report_worker() {
|
|
129
|
+
let boot = boot_test::<App, Migrator>().await.unwrap();
|
|
130
|
+
|
|
131
|
+
let result = ReportWorker::perform_later(
|
|
132
|
+
&boot.app_context,
|
|
133
|
+
ReportArgs { user_id: 1 },
|
|
134
|
+
)
|
|
135
|
+
.await;
|
|
136
|
+
|
|
137
|
+
assert!(result.is_ok());
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Snapshot Testing
|
|
144
|
+
|
|
145
|
+
Use `insta` to snapshot API responses and reduce manual assertions:
|
|
146
|
+
|
|
147
|
+
```rust
|
|
148
|
+
#[tokio::test]
|
|
149
|
+
#[serial]
|
|
150
|
+
async fn test_user_response_shape() {
|
|
151
|
+
request::<App, Migrator, _, _>(|request, ctx| async move {
|
|
152
|
+
let response = request.get("/api/users/1").await;
|
|
153
|
+
|
|
154
|
+
insta::assert_yaml_snapshot!(
|
|
155
|
+
response.json::<serde_json::Value>(),
|
|
156
|
+
{
|
|
157
|
+
".created_at" => "[timestamp]",
|
|
158
|
+
".updated_at" => "[timestamp]",
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
})
|
|
162
|
+
.await;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Test Organization
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
tests/
|
|
172
|
+
requests/ # Controller integration tests
|
|
173
|
+
posts.rs
|
|
174
|
+
auth.rs
|
|
175
|
+
models/ # Model unit tests
|
|
176
|
+
users.rs
|
|
177
|
+
posts.rs
|
|
178
|
+
workers/ # Worker tests
|
|
179
|
+
report_worker.rs
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Pattern**: Mirror `src/` structure in `tests/`.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Common Commands
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
cargo test # Run all tests
|
|
190
|
+
cargo nextest run # Faster parallel runner
|
|
191
|
+
cargo test -- --test-threads=1 # Serial execution (DB-dependent)
|
|
192
|
+
cargo test tests::requests::posts # Run specific module
|
|
193
|
+
cargo insta review # Review snapshot changes
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Anti-Patterns
|
|
199
|
+
|
|
200
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| Testing controller logic directly | Misses middleware, auth, routing | Use request tests via `loco_rs::testing` |
|
|
203
|
+
| Shared test database without truncation | Flaky tests from leftover data | Enable `dangerously_truncate` in test config |
|
|
204
|
+
| Testing auto-generated entities | Tests break on regeneration | Test your model methods, not SeaORM internals |
|
|
205
|
+
| `BackgroundAsync` in tests | Non-deterministic worker execution | Use `ForegroundBlocking` mode |
|
|
206
|
+
| Large assertion blocks | Brittle, hard to maintain | Use `insta` snapshot testing |
|
|
207
|
+
| Skipping auth in tests | Misses permission bugs | Use helper to create authenticated test requests |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
_Test models for correctness, controllers for integration, workers for isolation. Let generators create test scaffolding._
|