Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add happy-path test cases for Model and ApiClient #16

Merged
merged 6 commits into from
Jan 30, 2024
Merged

Conversation

natebosch
Copy link
Collaborator

Refactor ApiClient to go from Map to Map instead of bytes to
String. This makes it easier to write a stub client which is agnostic
to the ordering of values in the maps. Move the responsibilities for
UTF8 and JSON encoding to the HttpApiClient.

Eagerly call nested .toJson() methods for child fields in .toJson()
implementations. This allows deep equality checking for the stub client
without implementing operator == for the data model classes.

Add a createModelWithClient method in src/model.dart which will not
be exported to the package public API. Use it in the test to create a
model with a stubbed client.

Add tests for each call in GenerativeModel. Tests validate

  • Encoding from arguments to the JSON schema.
  • Building the correct Uri for the task and model.
  • Parsing the result to the Dart data model.

Add tests for unary and streaming calls in HttpClient. Tests validate

  • Headers are set correctly.
  • Encoding and decoding UTF8 and JSON.
  • Using streaming with alt=sse query parameter and parsing of data:
    lines in the response.

Add Matcher helper methods for classes checked during tests that don't
have operator ==.

Refactor `ApiClient` to go from `Map` to `Map` instead of bytes to
`String`. This makes it easier to write a stub client which is agnostic
to the ordering of values in the maps. Move the responsibilities for
UTF8 and JSON encoding to the `HttpApiClient`.

Eagerly call nested `.toJson()` methods for child fields in `.toJson()`
implementations. This allows deep equality checking for the stub client
without implementing `operator ==` for the data model classes.

Add a `createModelWithClient` method in `src/model.dart` which will not
be exported to the package public API. Use it in the test to create a
model with a stubbed client.

Add tests for each call in `GenerativeModel`. Tests validate
- Encoding from arguments to the JSON schema.
- Building the correct `Uri` for the task and model.
- Parsing the result to the Dart data model.

Add tests for unary and streaming calls in `HttpClient`. Tests validate
- Headers are set correctly.
- Encoding and decoding UTF8 and JSON.
- Using streaming with `alt=sse` query parameter and parsing of `data: `
  lines in the response.

Add Matcher helper methods for classes checked during tests that don't
have `operator ==`.
@natebosch natebosch requested a review from devoncarew January 27, 2024 00:26
Copy link
Collaborator

@devoncarew devoncarew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm; great refactoring for testability.

Feel free to ignore the tailing comma suggested edits, though I suspect some of them will help w/ idents and readability in the files.

I would comment back in the dart test check at the end of the google_generative_ai.yml workflow file before landing this.

headers: {..._headers, 'Content-Type': 'application/json'}, body: body);
return utf8.decode(response.bodyBytes);
headers: {..._headers, 'Content-Type': 'application/json'},
body: utf8.encode(jsonEncode(body)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for readability?

Suggested change
body: utf8.encode(jsonEncode(body)));
body: utf8.encode(jsonEncode(body)),);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it looks better that way. Hopefully soon we won't need to make these choices manually 🤞

{required String model,
required String apiKey,
List<SafetySetting> safetySettings = const [],
GenerationConfig? generationConfig}) =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GenerationConfig? generationConfig}) =>
GenerationConfig? generationConfig},) =>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return parseEmbedContentReponse(response);
}
}

GenerativeModel createModelwithClient(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used for testing; I'm not sure if this method is visible outside this library, but assuming it is, perhaps annotate with visibleForTesting? Good to document this in either case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment for now. Adding a dependency on package:meta is low-cost - but I don't think it's worth for just this annotation.

@@ -10,10 +10,11 @@ environment:
sdk: ^3.2.0

dependencies:
crypt: ^4.3.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 - nice to lose a dep

@@ -0,0 +1,154 @@
import 'package:google_generative_ai/google_generative_ai.dart';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: copyright header

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Done.

}
]
}
]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
]);
],);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added trailing commas for all these subsequent collection arguments.

Trailing commas.
Copyright headers.
Document testing-only
@natebosch natebosch merged commit 7863b48 into main Jan 30, 2024
7 checks passed
@natebosch natebosch deleted the happy-path-tests branch January 30, 2024 18:18
khanhnwin pushed a commit to khanhnwin/generative-ai-dart that referenced this pull request Feb 2, 2024
Refactor `ApiClient` to go from `Map` to `Map` instead of bytes to
`String`. This makes it easier to write a stub client which is agnostic
to the ordering of values in the maps. Move the responsibilities for
UTF8 and JSON encoding to the `HttpApiClient`.

Eagerly call nested `.toJson()` methods for child fields in `.toJson()`
implementations. This allows deep equality checking for the stub client
without implementing `operator ==` for the data model classes.

Add a `createModelWithClient` method in `src/model.dart` which will not
be exported to the package public API. Use it in the test to create a
model with a stubbed client.

Add tests for each call in `GenerativeModel`. Tests validate
- Encoding from arguments to the JSON schema.
- Building the correct `Uri` for the task and model.
- Parsing the result to the Dart data model.

Add tests for unary and streaming calls in `HttpClient`. Tests validate
- Headers are set correctly.
- Encoding and decoding UTF8 and JSON.
- Using streaming with `alt=sse` query parameter and parsing of `data: `
  lines in the response.

Add Matcher helper methods for classes checked during tests that don't
have `operator ==`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants