refactor: update socket client and clean up backtest analysis files
This commit is contained in:
parent
9efc0a68dc
commit
5d6a9617aa
4 changed files with 220 additions and 362 deletions
|
|
@ -1,133 +0,0 @@
|
|||
<!-- task=m-backtest-analysis-surface/03+01,02_api_client_boundary plan=0 tag=BAS-BOUNDARY -->
|
||||
|
||||
# Code Review Reference - BAS-BOUNDARY
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-05-30
|
||||
task=m-backtest-analysis-surface/03+01,02_api_client_boundary, plan=0, tag=BAS-BOUNDARY
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. active plan/review files를 `.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 `agent-task/archive/YYYY/MM/m-backtest-analysis-surface/03+01,02_api_client_boundary/`로 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정은 런타임 책임이다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [BAS-BOUNDARY-1] Align API parser boundary | [ ] |
|
||||
| [BAS-BOUNDARY-2] Add typed client request helpers | [ ] |
|
||||
| [BAS-BOUNDARY-3] Test backtest socket helpers | [ ] |
|
||||
| [BAS-BOUNDARY-4] Boundary verification | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [BAS-BOUNDARY-1] API parser map and tests include all analysis request/response messages if not already done by contracts task.
|
||||
- [ ] [BAS-BOUNDARY-2] `AltSocketClient` exposes typed backtest list/detail/result/compare request helpers.
|
||||
- [ ] [BAS-BOUNDARY-3] Flutter socket client tests verify request type names, payloads, and response parsing. 검증: worker/API/client 경계가 backtest result를 일관되게 다룬다.
|
||||
- [ ] [BAS-BOUNDARY-4] `go test ./services/api/...`, `cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart`, `bin/test`, `bin/lint`를 실행한다. 검증: `bin/test`와 `bin/lint`가 통과한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- Client helper request/response generic types match generated protobuf messages.
|
||||
- Fake websocket tests assert `PacketBase.typeName`, request payload, and parsed response.
|
||||
- API parser map remains aligned with client parser map for analysis messages.
|
||||
- No worker internals are imported into API or client.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
|
||||
### BAS-BOUNDARY-1 중간 검증
|
||||
```text
|
||||
$ go test ./services/api/...
|
||||
(output)
|
||||
```
|
||||
|
||||
### BAS-BOUNDARY-2 중간 검증
|
||||
```text
|
||||
$ cd apps/client && flutter analyze --no-fatal-infos
|
||||
(output)
|
||||
```
|
||||
|
||||
### BAS-BOUNDARY-3 중간 검증
|
||||
```text
|
||||
$ cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```text
|
||||
$ go test ./services/api/...
|
||||
$ cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart
|
||||
$ bin/test
|
||||
$ bin/lint
|
||||
$ git diff --check
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
<!-- task=m-backtest-analysis-surface/03+01,02_api_client_boundary plan=0 tag=BAS-BOUNDARY -->
|
||||
|
||||
# Plan - BAS-BOUNDARY
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고, active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 사용자 결정, 외부 환경 준비, 범위 충돌이 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 남기고 멈춘다. archive, `complete.log`, 최종 판정은 code-review 전용이다.
|
||||
|
||||
## 배경
|
||||
|
||||
Contract와 worker storage가 준비되더라도 operator client가 typed backtest query 요청을 보낼 수 없으면 analysis surface가 실제 사용 흐름으로 연결되지 않는다. 이 계획은 API parser boundary와 Flutter socket client request helpers를 좁게 연결해 run list/detail/compare 표면의 첫 호출 경계를 만든다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단 조건은 active `CODE_REVIEW-cloud-G06.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 이를 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/backtest-loop/milestones/backtest-analysis-surface.md`
|
||||
- `services/api/internal/contracts/parser_map.go`
|
||||
- `services/api/internal/contracts/parser_map_test.go`
|
||||
- `apps/client/lib/src/contracts/alt_contracts.dart`
|
||||
- `apps/client/test/contracts/alt_contracts_test.dart`
|
||||
- `apps/client/lib/src/integrations/socket/alt_socket_client.dart`
|
||||
- `apps/client/test/integrations/socket/alt_socket_client_test.dart`
|
||||
- `packages/contracts/proto/alt/v1/backtest.proto`
|
||||
- `bin/test`
|
||||
- `bin/lint`
|
||||
- `services/api/go.mod`
|
||||
- `apps/client/pubspec.yaml`
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `AltSocketClient` currently tests only `hello()` request/response at `apps/client/test/integrations/socket/alt_socket_client_test.dart:113`.
|
||||
- No client test sends backtest run list/detail/result/compare requests through proto-socket.
|
||||
- API parser tests cover current message types only and must remain aligned with generated messages from `01_contract_surface`.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- Renamed/removed symbols: none planned.
|
||||
- New client methods will call existing inherited `sendRequest` in `AltSocketClient`, currently used by `hello()` at `apps/client/lib/src/integrations/socket/alt_socket_client.dart:32`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- Split policy evaluated before writing plan files.
|
||||
- This task depends on `01_contract_surface` and `02+01_worker_analysis_store`.
|
||||
- It is separate because API/client boundary tests can be reviewed independently after schema and storage behavior exist.
|
||||
- It does not create full Flutter operator UI; that belongs to later `Flutter Operator Console`.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Include API parser alignment if new messages were not already covered by `01_contract_surface`, typed client request helpers, and socket client tests.
|
||||
- Exclude server-side business handlers because current API service only owns parser/socket boundary; worker owns execution and storage.
|
||||
- Exclude dashboard UI and navigation.
|
||||
- Exclude migrations/storage behavior already covered by `02+01_worker_analysis_store`.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build lane: `cloud-G06`; review lane: `cloud-G06`.
|
||||
- Rationale: cross-language boundary helpers and tests, but no storage or schema design should remain by this stage.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [BAS-BOUNDARY-1] API parser map and tests include all analysis request/response messages if not already done by contracts task.
|
||||
- [ ] [BAS-BOUNDARY-2] `AltSocketClient` exposes typed backtest list/detail/result/compare request helpers.
|
||||
- [ ] [BAS-BOUNDARY-3] Flutter socket client tests verify request type names, payloads, and response parsing. 검증: worker/API/client 경계가 backtest result를 일관되게 다룬다.
|
||||
- [ ] [BAS-BOUNDARY-4] `go test ./services/api/...`, `cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart`, `bin/test`, `bin/lint`를 실행한다. 검증: `bin/test`와 `bin/lint`가 통과한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [BAS-BOUNDARY-1] Align API parser boundary
|
||||
|
||||
문제: `services/api/internal/contracts/parser_map.go:19` currently registers existing backtest messages. If `01_contract_surface` added list/detail/compare messages and did not register them, API parser tests will not cover the analysis request surface.
|
||||
|
||||
해결 방법:
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
19 protoSocket.TypeNameOf(&altv1.StartBacktestRequest{}): parserFor(func() proto.Message { return &altv1.StartBacktestRequest{} }),
|
||||
21 protoSocket.TypeNameOf(&altv1.GetBacktestRunRequest{}): parserFor(func() proto.Message { return &altv1.GetBacktestRunRequest{} }),
|
||||
23 protoSocket.TypeNameOf(&altv1.GetBacktestResultRequest{}): parserFor(func() proto.Message { return &altv1.GetBacktestResultRequest{} }),
|
||||
```
|
||||
|
||||
After: ensure `ListBacktestRuns*`, `GetBacktestRunDetail*`, and `CompareBacktestRuns*` generated messages are present in `ParserMap()` and `TestParserMapIncludesAltMessages`.
|
||||
|
||||
수정 파일 및 체크리스트:
|
||||
|
||||
- [ ] `services/api/internal/contracts/parser_map.go` includes all analysis request/response messages.
|
||||
- [ ] `services/api/internal/contracts/parser_map_test.go` includes all analysis messages and round-trip parsing.
|
||||
|
||||
테스트 작성: update existing parser map test only.
|
||||
|
||||
중간 검증:
|
||||
|
||||
```bash
|
||||
go test ./services/api/...
|
||||
```
|
||||
|
||||
Expected: exits 0.
|
||||
|
||||
### [BAS-BOUNDARY-2] Add typed client request helpers
|
||||
|
||||
문제: `AltSocketClient.hello()` at `apps/client/lib/src/integrations/socket/alt_socket_client.dart:32` is the only typed helper. Backtest analysis requests would require callers to use raw generated messages directly.
|
||||
|
||||
해결 방법:
|
||||
|
||||
Before:
|
||||
|
||||
```dart
|
||||
32 Future<HelloResponse> hello({Duration timeout = const Duration(seconds: 2)}) {
|
||||
33 return sendRequest<HelloRequest, HelloResponse>(
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```dart
|
||||
Future<ListBacktestRunsResponse> listBacktestRuns(
|
||||
ListBacktestRunsRequest request, {
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) {
|
||||
return sendRequest<ListBacktestRunsRequest, ListBacktestRunsResponse>(
|
||||
request,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Add equivalent helpers for `GetBacktestRunDetailRequest`, existing `GetBacktestResultRequest`, and `CompareBacktestRunsRequest`.
|
||||
|
||||
수정 파일 및 체크리스트:
|
||||
|
||||
- [ ] `apps/client/lib/src/integrations/socket/alt_socket_client.dart` imports generated `backtest.pb.dart`.
|
||||
- [ ] Add `listBacktestRuns`, `getBacktestRunDetail`, `getBacktestResult`, and `compareBacktestRuns` helpers.
|
||||
- [ ] Keep default timeout consistent with `hello()`.
|
||||
|
||||
테스트 작성: tests in BAS-BOUNDARY-3.
|
||||
|
||||
중간 검증:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter analyze --no-fatal-infos
|
||||
```
|
||||
|
||||
Expected: exits 0.
|
||||
|
||||
### [BAS-BOUNDARY-3] Test backtest socket helpers
|
||||
|
||||
문제: `apps/client/test/integrations/socket/alt_socket_client_test.dart:113` verifies hello only; no test asserts backtest request type names or response parsing over `PacketBase`.
|
||||
|
||||
해결 방법: Extend fake websocket tests with one helper to assert sent packet type/payload and feed a matching response packet. Cover at least list runs, run detail, get result, and compare runs.
|
||||
|
||||
수정 파일 및 체크리스트:
|
||||
|
||||
- [ ] `apps/client/test/integrations/socket/alt_socket_client_test.dart` imports generated `backtest.pb.dart`.
|
||||
- [ ] Add tests for each new client helper.
|
||||
- [ ] Tests assert sent `PacketBase.typeName`, serialized request fields, and parsed response fields.
|
||||
|
||||
테스트 작성: add focused tests in existing `AltSocketClient tests` group.
|
||||
|
||||
중간 검증:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart
|
||||
```
|
||||
|
||||
Expected: exits 0.
|
||||
|
||||
### [BAS-BOUNDARY-4] Boundary verification
|
||||
|
||||
문제: API parser, generated contract, and client helper changes must remain consistent across the workspace.
|
||||
|
||||
해결 방법: run focused API/client checks and full workspace smoke.
|
||||
|
||||
수정 파일 및 체크리스트:
|
||||
|
||||
- [ ] Run API parser tests.
|
||||
- [ ] Run client socket helper tests.
|
||||
- [ ] Run full `bin/test`.
|
||||
- [ ] Run full `bin/lint`.
|
||||
- [ ] Run `git diff --check`.
|
||||
|
||||
테스트 작성: no extra tests beyond BAS-BOUNDARY-1 and BAS-BOUNDARY-3.
|
||||
|
||||
중간 검증:
|
||||
|
||||
```bash
|
||||
go test ./services/api/...
|
||||
cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart
|
||||
bin/test
|
||||
bin/lint
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: all commands exit 0. Go test cache output is acceptable after focused API tests pass; Flutter test output must be actual command output.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
Directory dependency `03+01,02_api_client_boundary` means both predecessor task directories must produce `complete.log` before this task starts:
|
||||
|
||||
- `agent-task/m-backtest-analysis-surface/01_contract_surface/complete.log`
|
||||
- `agent-task/m-backtest-analysis-surface/02+01_worker_analysis_store/complete.log`
|
||||
|
||||
Do not begin if either file is absent.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `services/api/internal/contracts/parser_map.go` | BAS-BOUNDARY-1 |
|
||||
| `services/api/internal/contracts/parser_map_test.go` | BAS-BOUNDARY-1 |
|
||||
| `apps/client/lib/src/integrations/socket/alt_socket_client.dart` | BAS-BOUNDARY-2 |
|
||||
| `apps/client/test/integrations/socket/alt_socket_client_test.dart` | BAS-BOUNDARY-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test ./services/api/...
|
||||
cd apps/client && flutter test test/integrations/socket/alt_socket_client_test.dart
|
||||
bin/test
|
||||
bin/lint
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: all commands exit 0. Go test cache output is acceptable after focused API tests pass; Flutter test output must be actual command output.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'package:proto_socket/proto_socket.dart';
|
||||
|
||||
import 'package:alt_client/src/contracts/alt_contracts.dart';
|
||||
import 'package:alt_client/src/generated/alt/v1/backtest.pb.dart';
|
||||
import 'package:alt_client/src/generated/alt/v1/common.pb.dart';
|
||||
import 'socket_endpoint.dart';
|
||||
|
||||
|
|
@ -38,4 +39,45 @@ class AltSocketClient extends WsProtobufClient {
|
|||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ListBacktestRunsResponse> listBacktestRuns(
|
||||
ListBacktestRunsRequest request, {
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) {
|
||||
return sendRequest<ListBacktestRunsRequest, ListBacktestRunsResponse>(
|
||||
request,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
Future<GetBacktestRunDetailResponse> getBacktestRunDetail(
|
||||
GetBacktestRunDetailRequest request, {
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) {
|
||||
return sendRequest<GetBacktestRunDetailRequest,
|
||||
GetBacktestRunDetailResponse>(
|
||||
request,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
Future<GetBacktestResultResponse> getBacktestResult(
|
||||
GetBacktestResultRequest request, {
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) {
|
||||
return sendRequest<GetBacktestResultRequest, GetBacktestResultResponse>(
|
||||
request,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CompareBacktestRunsResponse> compareBacktestRuns(
|
||||
CompareBacktestRunsRequest request, {
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) {
|
||||
return sendRequest<CompareBacktestRunsRequest, CompareBacktestRunsResponse>(
|
||||
request,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:proto_socket/proto_socket.dart';
|
||||
|
||||
import 'package:alt_client/src/generated/alt/v1/backtest.pb.dart';
|
||||
import 'package:alt_client/src/generated/alt/v1/common.pb.dart';
|
||||
import 'package:alt_client/src/integrations/socket/alt_socket_client.dart';
|
||||
import 'package:alt_client/src/integrations/socket/socket_endpoint.dart';
|
||||
|
|
@ -147,5 +148,182 @@ void main() {
|
|||
expect(response.serverVersion, equals('1.0.0'));
|
||||
expect(response.altProtocolVersion, equals('alt.v1'));
|
||||
});
|
||||
|
||||
// Drains the request sent by [trigger], asserts the sent packet type/payload
|
||||
// through [onSent], then feeds a matching response packet back so the worker
|
||||
// -> API -> client backtest boundary round-trips consistently.
|
||||
Future<PacketBase> roundTrip({
|
||||
required Future<void> Function() trigger,
|
||||
required void Function(PacketBase sentPacket) onSent,
|
||||
required String responseTypeName,
|
||||
required List<int> responseData,
|
||||
}) async {
|
||||
final sentCountBefore = fakeWs.sentBytes.length;
|
||||
unawaited(trigger());
|
||||
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
expect(fakeWs.sentBytes.length, greaterThan(sentCountBefore));
|
||||
final sentPacket = PacketBase.fromBuffer(fakeWs.sentBytes.last);
|
||||
onSent(sentPacket);
|
||||
|
||||
final responsePacket = PacketBase()
|
||||
..typeName = responseTypeName
|
||||
..nonce = 200
|
||||
..responseNonce = sentPacket.nonce
|
||||
..data = responseData;
|
||||
fakeWs.feedFromServer(responsePacket.writeToBuffer());
|
||||
|
||||
return sentPacket;
|
||||
}
|
||||
|
||||
test('listBacktestRuns request-response loop', () async {
|
||||
final request = ListBacktestRunsRequest()
|
||||
..status = BacktestRunStatus.BACKTEST_RUN_STATUS_SUCCEEDED;
|
||||
final expectedResponse = ListBacktestRunsResponse()
|
||||
..runs.add(BacktestRun()
|
||||
..id = 'run-1'
|
||||
..status = BacktestRunStatus.BACKTEST_RUN_STATUS_SUCCEEDED);
|
||||
|
||||
final future = client.listBacktestRuns(
|
||||
request,
|
||||
timeout: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
await roundTrip(
|
||||
trigger: () => future,
|
||||
onSent: (sentPacket) {
|
||||
expect(
|
||||
sentPacket.typeName,
|
||||
equals(
|
||||
ListBacktestRunsRequest.getDefault().info_.qualifiedMessageName,
|
||||
),
|
||||
);
|
||||
final sentRequest = ListBacktestRunsRequest.fromBuffer(sentPacket.data);
|
||||
expect(
|
||||
sentRequest.status,
|
||||
equals(BacktestRunStatus.BACKTEST_RUN_STATUS_SUCCEEDED),
|
||||
);
|
||||
},
|
||||
responseTypeName:
|
||||
ListBacktestRunsResponse.getDefault().info_.qualifiedMessageName,
|
||||
responseData: expectedResponse.writeToBuffer(),
|
||||
);
|
||||
|
||||
final response = await future;
|
||||
expect(response.runs, hasLength(1));
|
||||
expect(response.runs.first.id, equals('run-1'));
|
||||
expect(
|
||||
response.runs.first.status,
|
||||
equals(BacktestRunStatus.BACKTEST_RUN_STATUS_SUCCEEDED),
|
||||
);
|
||||
});
|
||||
|
||||
test('getBacktestRunDetail request-response loop', () async {
|
||||
final request = GetBacktestRunDetailRequest()..runId = 'run-7';
|
||||
final expectedResponse = GetBacktestRunDetailResponse()
|
||||
..run = (BacktestRun()..id = 'run-7')
|
||||
..result = (BacktestResult()..runId = 'run-7');
|
||||
|
||||
final future = client.getBacktestRunDetail(
|
||||
request,
|
||||
timeout: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
await roundTrip(
|
||||
trigger: () => future,
|
||||
onSent: (sentPacket) {
|
||||
expect(
|
||||
sentPacket.typeName,
|
||||
equals(
|
||||
GetBacktestRunDetailRequest.getDefault()
|
||||
.info_
|
||||
.qualifiedMessageName,
|
||||
),
|
||||
);
|
||||
final sentRequest =
|
||||
GetBacktestRunDetailRequest.fromBuffer(sentPacket.data);
|
||||
expect(sentRequest.runId, equals('run-7'));
|
||||
},
|
||||
responseTypeName: GetBacktestRunDetailResponse.getDefault()
|
||||
.info_
|
||||
.qualifiedMessageName,
|
||||
responseData: expectedResponse.writeToBuffer(),
|
||||
);
|
||||
|
||||
final response = await future;
|
||||
expect(response.run.id, equals('run-7'));
|
||||
expect(response.result.runId, equals('run-7'));
|
||||
});
|
||||
|
||||
test('getBacktestResult request-response loop', () async {
|
||||
final request = GetBacktestResultRequest()..runId = 'run-9';
|
||||
final expectedResponse = GetBacktestResultResponse()
|
||||
..result = (BacktestResult()..runId = 'run-9');
|
||||
|
||||
final future = client.getBacktestResult(
|
||||
request,
|
||||
timeout: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
await roundTrip(
|
||||
trigger: () => future,
|
||||
onSent: (sentPacket) {
|
||||
expect(
|
||||
sentPacket.typeName,
|
||||
equals(
|
||||
GetBacktestResultRequest.getDefault().info_.qualifiedMessageName,
|
||||
),
|
||||
);
|
||||
final sentRequest =
|
||||
GetBacktestResultRequest.fromBuffer(sentPacket.data);
|
||||
expect(sentRequest.runId, equals('run-9'));
|
||||
},
|
||||
responseTypeName:
|
||||
GetBacktestResultResponse.getDefault().info_.qualifiedMessageName,
|
||||
responseData: expectedResponse.writeToBuffer(),
|
||||
);
|
||||
|
||||
final response = await future;
|
||||
expect(response.result.runId, equals('run-9'));
|
||||
});
|
||||
|
||||
test('compareBacktestRuns request-response loop', () async {
|
||||
final request = CompareBacktestRunsRequest()
|
||||
..runIds.addAll(['run-1', 'run-2']);
|
||||
final expectedResponse = CompareBacktestRunsResponse()
|
||||
..results.add(BacktestResult()..runId = 'run-1')
|
||||
..results.add(BacktestResult()..runId = 'run-2');
|
||||
|
||||
final future = client.compareBacktestRuns(
|
||||
request,
|
||||
timeout: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
await roundTrip(
|
||||
trigger: () => future,
|
||||
onSent: (sentPacket) {
|
||||
expect(
|
||||
sentPacket.typeName,
|
||||
equals(
|
||||
CompareBacktestRunsRequest.getDefault().info_.qualifiedMessageName,
|
||||
),
|
||||
);
|
||||
final sentRequest =
|
||||
CompareBacktestRunsRequest.fromBuffer(sentPacket.data);
|
||||
expect(sentRequest.runIds, equals(['run-1', 'run-2']));
|
||||
},
|
||||
responseTypeName:
|
||||
CompareBacktestRunsResponse.getDefault().info_.qualifiedMessageName,
|
||||
responseData: expectedResponse.writeToBuffer(),
|
||||
);
|
||||
|
||||
final response = await future;
|
||||
expect(response.results, hasLength(2));
|
||||
expect(
|
||||
response.results.map((r) => r.runId),
|
||||
equals(['run-1', 'run-2']),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue