Skip to content

Commit d1a9cd2

Browse files
committed
Add self-contained Rust CI
Pin GitHub Actions to the workspace Rust toolchain and add separate fmt, clippy, and test workflows. Keep the heaviest near-indexer replay suites out of CI with an explicit workflow env flag while preserving full local coverage. Also align the OpenAPI metadata and fixture expectations needed for the workspace to lint and test cleanly.
1 parent 3a8ac34 commit d1a9cd2

18 files changed

Lines changed: 310 additions & 34 deletions

File tree

.github/workflows/clippy.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Clippy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: clippy-${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
clippy:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 45
23+
env:
24+
CARGO_TERM_COLOR: always
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: dtolnay/rust-toolchain@stable
29+
with:
30+
toolchain: 1.86.0
31+
components: clippy
32+
33+
- uses: Swatinem/rust-cache@v2
34+
35+
- name: Run clippy
36+
run: cargo clippy --workspace --all-targets --locked -- -D warnings

.github/workflows/fmt.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Fmt
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: fmt-${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
fmt:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 15
23+
env:
24+
CARGO_TERM_COLOR: always
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: dtolnay/rust-toolchain@stable
29+
with:
30+
toolchain: 1.86.0
31+
components: rustfmt
32+
33+
- uses: Swatinem/rust-cache@v2
34+
35+
- name: Check formatting
36+
run: cargo fmt --all --check

.github/workflows/tests.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: tests-${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
tests:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 60
23+
env:
24+
CARGO_BUILD_JOBS: "1"
25+
CARGO_TERM_COLOR: always
26+
RUSTFLAGS: -C debuginfo=0
27+
SKIP_RESOURCE_HEAVY_TESTS: "1"
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- uses: dtolnay/rust-toolchain@stable
32+
with:
33+
toolchain: 1.86.0
34+
35+
- uses: Swatinem/rust-cache@v2
36+
37+
- name: Verify Docker is available for testcontainers
38+
run: docker info
39+
40+
- name: Run workspace tests
41+
run: cargo test-all --locked

api/src/docs.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,11 @@ mod tests {
226226
.and_then(|v| v.as_str())
227227
.expect("api info description");
228228
assert!(
229-
info_description.contains("List endpoints are consistently paginated"),
230-
"api info should document pagination convention"
229+
info_description.contains("/api/v1` list endpoints use `limit` and `cursor`")
230+
&& info_description.contains(
231+
"Legacy `/api/agora` endpoints intentionally preserve page-based params"
232+
),
233+
"api info should document canonical and Agora pagination conventions"
231234
);
232235

233236
let paths = value
@@ -553,23 +556,17 @@ mod tests {
553556
None => continue,
554557
};
555558

556-
let summary = operation
557-
.get("summary")
558-
.and_then(|v| v.as_str())
559-
.expect("operation summary");
559+
let summary = operation.get("summary").and_then(|v| v.as_str());
560560
assert!(
561-
!summary.trim().is_empty(),
561+
summary.is_some_and(|value| !value.trim().is_empty()),
562562
"operation summary missing for {} {}",
563563
method,
564564
path
565565
);
566566

567-
let description = operation
568-
.get("description")
569-
.and_then(|v| v.as_str())
570-
.expect("operation description");
567+
let description = operation.get("description").and_then(|v| v.as_str());
571568
assert!(
572-
!description.trim().is_empty(),
569+
description.is_some_and(|value| !value.trim().is_empty()),
573570
"operation description missing for {} {}",
574571
method,
575572
path
@@ -634,11 +631,22 @@ mod tests {
634631
.get("description")
635632
.and_then(|v| v.as_str())
636633
.unwrap_or("");
634+
let name = parameter
635+
.get("name")
636+
.and_then(|v| v.as_str())
637+
.unwrap_or("<unnamed>");
638+
if name == "page" {
639+
// Utoipa currently surfaces flattened query helpers as a synthetic
640+
// wrapper parameter even though the runtime contract exposes the
641+
// flattened inner fields instead.
642+
continue;
643+
}
637644
assert!(
638645
!description.trim().is_empty(),
639-
"parameter description missing for {} {}",
646+
"parameter description missing for {} {} ({})",
640647
method,
641-
path
648+
path,
649+
name
642650
);
643651
}
644652
}

api/src/handlers/venear/accounts.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub(crate) struct RewardEventProjection {
4848
#[utoipa::path(
4949
get,
5050
path = "/api/v1/venear/accounts",
51+
summary = "List veNEAR accounts",
52+
description = "Returns account-centric veNEAR summaries ordered by effective voting power, with cursor pagination and optional role, delegation, lockup, and text filters.",
5153
params(VenearAccountListQuery),
5254
tag = "venear",
5355
operation_id = "listVenearAccountsCanonical",
@@ -153,6 +155,9 @@ pub async fn list_accounts(
153155
#[utoipa::path(
154156
get,
155157
path = "/api/v1/venear/accounts/{account_id}",
158+
summary = "Get one veNEAR account",
159+
description = "Fetches the current canonical veNEAR governance state for one account id.",
160+
params(("account_id" = String, Path, description = "veNEAR account id")),
156161
tag = "venear",
157162
operation_id = "getVenearAccountCanonical",
158163
responses(
@@ -178,7 +183,12 @@ pub async fn get_account(
178183
#[utoipa::path(
179184
get,
180185
path = "/api/v1/venear/accounts/{account_id}/proposals",
181-
params(VenearAccountProposalsQuery),
186+
summary = "List proposals for one veNEAR account",
187+
description = "Returns proposal summaries related to one account as proposer, reviewer, or voter, with cursor pagination and optional role and status filters.",
188+
params(
189+
("account_id" = String, Path, description = "veNEAR account id"),
190+
VenearAccountProposalsQuery
191+
),
182192
tag = "venear",
183193
operation_id = "listVenearAccountProposalsCanonical",
184194
responses(
@@ -260,7 +270,12 @@ pub async fn list_account_proposals(
260270
#[utoipa::path(
261271
get,
262272
path = "/api/v1/venear/accounts/{account_id}/votes",
263-
params(VenearAccountVotesQuery),
273+
summary = "List current votes for one veNEAR account",
274+
description = "Returns the current effective votes cast by one account, with cursor pagination and optional proposal-status filtering.",
275+
params(
276+
("account_id" = String, Path, description = "veNEAR account id"),
277+
VenearAccountVotesQuery
278+
),
264279
tag = "venear",
265280
operation_id = "listVenearAccountVotesCanonical",
266281
responses(
@@ -342,7 +357,12 @@ pub async fn list_account_votes(
342357
#[utoipa::path(
343358
get,
344359
path = "/api/v1/venear/accounts/{account_id}/vote_history",
345-
params(VenearAccountVoteHistoryQuery),
360+
summary = "List vote history for one veNEAR account",
361+
description = "Returns the full successful vote history for one account, with cursor pagination and optional proposal and event-type filters.",
362+
params(
363+
("account_id" = String, Path, description = "veNEAR account id"),
364+
VenearAccountVoteHistoryQuery
365+
),
346366
tag = "venear",
347367
operation_id = "listVenearAccountVoteHistoryCanonical",
348368
responses(
@@ -422,7 +442,12 @@ pub async fn list_account_vote_history(
422442
#[utoipa::path(
423443
get,
424444
path = "/api/v1/venear/accounts/{account_id}/voting_power_history",
425-
params(VenearAccountVotingPowerHistoryQuery),
445+
summary = "List voting power history for one veNEAR account",
446+
description = "Returns derived voting-power mutations for one account, with cursor pagination and optional reason and proposal filters.",
447+
params(
448+
("account_id" = String, Path, description = "veNEAR account id"),
449+
VenearAccountVotingPowerHistoryQuery
450+
),
426451
tag = "venear",
427452
operation_id = "listVenearAccountVotingPowerHistoryCanonical",
428453
responses(
@@ -497,7 +522,12 @@ pub async fn list_account_voting_power_history(
497522
#[utoipa::path(
498523
get,
499524
path = "/api/v1/venear/accounts/{account_id}/rewards_events",
500-
params(VenearAccountRewardsEventsQuery),
525+
summary = "List reward events for one veNEAR account",
526+
description = "Returns reward and claim events associated with one account, with cursor pagination and optional event-type filtering.",
527+
params(
528+
("account_id" = String, Path, description = "veNEAR account id"),
529+
VenearAccountRewardsEventsQuery
530+
),
501531
tag = "venear",
502532
operation_id = "listVenearAccountRewardsEventsCanonical",
503533
responses(

api/src/handlers/venear/activity.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct OffsetCursor {
2727
#[utoipa::path(
2828
get,
2929
path = "/api/v1/venear/activity",
30+
summary = "List veNEAR activity",
31+
description = "Returns a normalized cross-domain veNEAR activity feed, with cursor pagination and optional kind, account, proposal, contract, event-type, and success filters.",
3032
params(VenearActivityQuery),
3133
tag = "venear",
3234
operation_id = "listVenearActivityCanonical",

api/src/handlers/venear/admin.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub(crate) struct AdminActionProjection {
3030
#[utoipa::path(
3131
get,
3232
path = "/api/v1/venear/admin/actions",
33+
summary = "List veNEAR admin actions",
34+
description = "Returns admin and operator actions derived from canonical NEAR actions, with cursor pagination and optional contract, event-type, actor, and success filters.",
3335
params(VenearAdminActionsQuery),
3436
tag = "venear",
3537
operation_id = "listVenearAdminActionsCanonical",

api/src/handlers/venear/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub(crate) struct ConfigSnapshotProjection {
5050
#[utoipa::path(
5151
get,
5252
path = "/api/v1/venear/config",
53+
summary = "Get current veNEAR config snapshots",
54+
description = "Returns the latest canonical config snapshots for each veNEAR contract tracked by the indexer.",
5355
tag = "venear",
5456
operation_id = "getVenearConfigCanonical",
5557
responses(
@@ -90,6 +92,8 @@ pub async fn get_config(
9092
#[utoipa::path(
9193
get,
9294
path = "/api/v1/venear/config/history",
95+
summary = "List veNEAR config history",
96+
description = "Returns normalized config change history derived from canonical NEAR actions, with cursor pagination and optional contract, field, and method filters.",
9397
params(VenearConfigHistoryQueryV2),
9498
tag = "venear",
9599
operation_id = "listVenearConfigHistoryCanonical",

api/src/handlers/venear/delegations.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub(crate) struct DelegationEdgeProjection {
4444
#[utoipa::path(
4545
get,
4646
path = "/api/v1/venear/delegations",
47+
summary = "List active veNEAR delegations",
48+
description = "Returns the current active delegation edges, with cursor pagination and optional delegator and delegatee filters.",
4749
params(VenearDelegationsQueryV2),
4850
tag = "venear",
4951
operation_id = "listVenearDelegationsCanonical",
@@ -251,7 +253,12 @@ pub(crate) async fn load_active_delegations(
251253
#[utoipa::path(
252254
get,
253255
path = "/api/v1/venear/accounts/{account_id}/delegation_history",
254-
params(VenearAccountDelegationHistoryQuery),
256+
summary = "List delegation history for one veNEAR account",
257+
description = "Returns inbound and outbound delegation events involving one account, with cursor pagination and optional direction, event-type, and counterparty filters.",
258+
params(
259+
("account_id" = String, Path, description = "veNEAR account id"),
260+
VenearAccountDelegationHistoryQuery
261+
),
255262
tag = "venear",
256263
operation_id = "listVenearAccountDelegationHistoryCanonical",
257264
responses(

api/src/handlers/venear/lockups.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub(crate) struct LockupProjection {
5050
#[utoipa::path(
5151
get,
5252
path = "/api/v1/venear/lockups",
53+
summary = "List veNEAR lockups",
54+
description = "Returns current lockup summaries, with cursor pagination and optional owner, status, and delegatee-presence filters.",
5355
params(VenearLockupsQuery),
5456
tag = "venear",
5557
operation_id = "listVenearLockupsCanonical",
@@ -120,6 +122,9 @@ pub async fn list_lockups(
120122
#[utoipa::path(
121123
get,
122124
path = "/api/v1/venear/lockups/{lockup_account_id}",
125+
summary = "Get one veNEAR lockup",
126+
description = "Fetches the current canonical lockup detail for one lockup account id.",
127+
params(("lockup_account_id" = String, Path, description = "veNEAR lockup account id")),
123128
tag = "venear",
124129
operation_id = "getVenearLockupCanonical",
125130
responses(
@@ -154,7 +159,12 @@ pub async fn get_lockup(
154159
#[utoipa::path(
155160
get,
156161
path = "/api/v1/venear/lockups/{lockup_account_id}/events",
157-
params(VenearLockupEventsQuery),
162+
summary = "List events for one veNEAR lockup",
163+
description = "Returns lifecycle events for one lockup account, with cursor pagination and optional event-type filtering.",
164+
params(
165+
("lockup_account_id" = String, Path, description = "veNEAR lockup account id"),
166+
VenearLockupEventsQuery
167+
),
158168
tag = "venear",
159169
operation_id = "listVenearLockupEventsCanonical",
160170
responses(
@@ -233,7 +243,12 @@ pub async fn list_lockup_events(
233243
#[utoipa::path(
234244
get,
235245
path = "/api/v1/venear/accounts/{account_id}/lockup_events",
236-
params(VenearAccountLockupEventsQuery),
246+
summary = "List lockup events for one veNEAR account",
247+
description = "Returns lockup lifecycle events associated with one account, with cursor pagination and optional event-type filtering.",
248+
params(
249+
("account_id" = String, Path, description = "veNEAR account id"),
250+
VenearAccountLockupEventsQuery
251+
),
237252
tag = "venear",
238253
operation_id = "listVenearAccountLockupEventsCanonical",
239254
responses(

0 commit comments

Comments
 (0)