refactor: update socket client and clean up backtest analysis files

This commit is contained in:
toki 2026-05-30 16:18:29 +09:00
parent 9efc0a68dc
commit 5d6a9617aa
4 changed files with 220 additions and 362 deletions

View file

@ -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.

View file

@ -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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.

View file

@ -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,
);
}
}

View file

@ -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']),
);
});
});
}