Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
55b25d0
feat: add unit test suite and Docker CI workflow
pthmas Mar 3, 2026
6f9d520
fix: apply cargo fmt formatting to test code
pthmas Mar 3, 2026
d68916c
ci: split backend into parallel fmt/clippy/test jobs
pthmas Mar 3, 2026
b747aa8
ci: parallelize Docker builds and add indexer cache
pthmas Mar 3, 2026
9a87f66
ci: use cargo-chef to cache Docker dependency compilation
pthmas Mar 3, 2026
d5d1089
fix: use pre-built cargo-chef image compatible with Rust 1.85
pthmas Mar 3, 2026
27b8a9f
fix: run cargo update downgrades before cargo-chef prepare
pthmas Mar 3, 2026
4bad997
revert: remove cargo-chef, restore simple Dockerfile
pthmas Mar 3, 2026
4fe1338
docs: add unit testing convention to CLAUDE.md
pthmas Mar 3, 2026
1f3a95f
ci: pin Docker workflow actions to commit SHAs and add Dependabot
pthmas Mar 3, 2026
6c884c5
revert: use version tags instead of SHA pins for trusted actions
pthmas Mar 3, 2026
11f379d
test: remove low-value tests, keep tests that cover real logic
pthmas Mar 4, 2026
0027e59
test: remove tests that only verify static mappings and stdlib behavior
pthmas Mar 4, 2026
90b8adc
fix: remove trailing blank line in error.rs after test block removal
pthmas Mar 4, 2026
87521b5
test: remove offset and pagination tests that only verify stdlib arit…
pthmas Mar 4, 2026
deff87a
test: remove low-value tests per review feedback
pthmas Mar 5, 2026
78f8a31
test: trim to 12 high-value tests, remove trivial insert/arithmetic c…
pthmas Mar 5, 2026
4e8b8f5
test: add 4 high-value tests for balance aggregation, contract detect…
pthmas Mar 5, 2026
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
45 changes: 39 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ concurrency:
cancel-in-progress: true

jobs:
backend:
name: Backend (Rust)
backend-fmt:
name: Backend Format
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -24,19 +24,52 @@ jobs:
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
components: rustfmt

- name: Format
run: cargo fmt --all --check

backend-clippy:
name: Backend Clippy
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: backend

- name: Format
run: cargo fmt --all --check

- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings

backend-test:
name: Backend Test
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: backend

- name: Test
run: cargo test --workspace --all-targets

Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Docker Build

on:
pull_request:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-indexer:
name: Indexer Docker (linux/amd64)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build indexer image
uses: docker/build-push-action@v6
with:
context: backend
target: indexer
platforms: linux/amd64
cache-from: type=gha,scope=backend
cache-to: type=gha,scope=backend,mode=max
outputs: type=cacheonly

build-api:
name: API Docker (linux/amd64)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build api image
uses: docker/build-push-action@v6
with:
context: backend
target: api
platforms: linux/amd64
cache-from: type=gha,scope=backend
cache-to: type=gha,scope=backend,mode=max
outputs: type=cacheonly

build-frontend:
name: Frontend Docker (linux/amd64)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build frontend image
uses: docker/build-push-action@v6
with:
context: frontend
platforms: linux/amd64
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=cacheonly
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub struct AppState {
- **Migrations**: use `run_migrations(&database_url)` (not `&pool`) to get a timeout-free connection
- **Frontend**: uses Bun (not npm/yarn). Lockfile is `bun.lock` (text, Bun ≥ 1.2). Build with `bunx vite build` (skips tsc type check).
- **Docker**: frontend image uses `nginxinc/nginx-unprivileged:alpine` (non-root, port 8080). API/indexer use `alpine` with `ca-certificates`.
- **Tests**: add unit tests for new logic in a `#[cfg(test)] mod tests` block in the same file. Run with `cargo test --workspace`.
- **Commits**: authored by the user only — no Claude co-author lines.

## Environment Variables
Expand Down
76 changes: 76 additions & 0 deletions backend/crates/atlas-indexer/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,79 @@ impl BlockBatch {
entry.last_block = entry.last_block.max(block);
}
}

#[cfg(test)]
mod tests {
use super::*;
use bigdecimal::BigDecimal;

// --- touch_addr tests ---

#[test]
fn touch_addr_keeps_minimum_first_seen_block() {
let mut batch = BlockBatch::new();
batch.touch_addr("0xabc".to_string(), 200, false, 0);
batch.touch_addr("0xabc".to_string(), 100, false, 0);

assert_eq!(batch.addr_map["0xabc"].first_seen_block, 100);
}

#[test]
fn touch_addr_is_contract_latches_true() {
let mut batch = BlockBatch::new();
batch.touch_addr("0xabc".to_string(), 100, false, 0);
batch.touch_addr("0xabc".to_string(), 101, true, 0);

assert!(batch.addr_map["0xabc"].is_contract);
}

// --- apply_balance_delta tests ---

#[test]
fn apply_balance_delta_accumulates_positive() {
let mut batch = BlockBatch::new();
batch.apply_balance_delta(
"0xaddr".to_string(),
"0xtoken".to_string(),
BigDecimal::from(100),
50,
);
batch.apply_balance_delta(
"0xaddr".to_string(),
"0xtoken".to_string(),
BigDecimal::from(50),
60,
);

let entry = batch
.balance_map
.get(&("0xaddr".to_string(), "0xtoken".to_string()))
.unwrap();
assert_eq!(entry.delta, BigDecimal::from(150));
assert_eq!(entry.last_block, 60);
}

#[test]
fn apply_balance_delta_tracks_max_block() {
let mut batch = BlockBatch::new();
batch.apply_balance_delta(
"0xaddr".to_string(),
"0xtoken".to_string(),
BigDecimal::from(1),
100,
);
// Earlier block — last_block should stay at 100
batch.apply_balance_delta(
"0xaddr".to_string(),
"0xtoken".to_string(),
BigDecimal::from(1),
50,
);

let entry = batch
.balance_map
.get(&("0xaddr".to_string(), "0xtoken".to_string()))
.unwrap();
assert_eq!(entry.last_block, 100);
}
}
Loading