Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 0.3.1

- `StatboticsTeamEvent.toJson` now serializes `team_name`, so the
`toJson`/`fromJson` round trip preserves the nickname it advertises.
Previously the field was decoded but left out of `toJson`, so a
cached record came back from the on-device last-good cache with an empty
`teamName` even though `/team_events` had carried one. Only `team_name` was at
risk; every other field already round-tripped.

## 0.3.0

- `StatboticsTeamEvent.teamName` exposes the nickname `/team_events` already
Expand Down
1 change: 1 addition & 0 deletions lib/src/statbotics_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class StatboticsTeamEvent {
'team': team,
'event': event,
'event_name': eventName,
'team_name': teamName,
'year': year,
'wins': wins,
'losses': losses,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: statbotics_client
description: A typed Dart client for the Statbotics API v3 (FRC EPA statistics, events, matches). Pure Dart, no Flutter dependency.
version: 0.3.0
version: 0.3.1
repository: https://github.com/Project516/statbotics_client

topics:
Expand Down
52 changes: 52 additions & 0 deletions test/statbotics_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,58 @@ void main() {
expect(te.teamName, isEmpty);
});

test('StatboticsTeamEvent.toJson round-trips team_name', () {
// toJson advertised a round-trip for the on-device last-good cache, but
// omitted team_name, so a cached StatboticsTeamEvent came back with an
// empty nickname. The cache key Statbotics itself does not store is the
// one event-scoped name source (/teams ignores its event param), so the
// loss was silent.
final original = StatboticsTeamEvent.fromJson(<String, dynamic>{
'team': 3847,
'event': '2026txhou',
'event_name': 'Houston',
'team_name': 'Spectrum',
'year': 2026,
'wins': 1,
'losses': 0,
'ties': 0,
'rank': 7,
'num_teams': 42,
'epa': <String, dynamic>{
'total_points': <String, dynamic>{'mean': 41.2, 'sd': 2.9},
'auto_points': <String, dynamic>{'mean': 9.5},
},
});
final restored = StatboticsTeamEvent.fromJson(original.toJson());
expect(restored.teamName, 'Spectrum');
expect(restored.team, 3847);
expect(restored.event, '2026txhou');
expect(restored.eventName, 'Houston');
expect(restored.year, 2026);
expect(restored.wins, 1);
expect(restored.losses, 0);
expect(restored.ties, 0);
expect(restored.rank, 7);
expect(restored.numTeams, 42);
expect(restored.epa.totalPointsMean, closeTo(41.2, 0.01));
expect(restored.epa.autoPointsMean, closeTo(9.5, 0.01));
});

test('StatboticsTeamEvent.toJson round-trips an empty team_name', () {
final original = StatboticsTeamEvent.fromJson(<String, dynamic>{
'team': 111,
'event': '2026x',
'event_name': 'X',
'year': 2026,
'wins': 0,
'losses': 0,
'ties': 0,
'epa': <String, dynamic>{},
});
final restored = StatboticsTeamEvent.fromJson(original.toJson());
expect(restored.teamName, isEmpty);
});

test('StatboticsTeamEvent.record omits ties when zero', () {
final te = StatboticsTeamEvent.fromJson(<String, dynamic>{
'team': 1234,
Expand Down