diff --git a/AGENT.md b/AGENT.md index 5ab8ef4..5c83953 100644 --- a/AGENT.md +++ b/AGENT.md @@ -7,14 +7,33 @@ - `dart compile exe bin/raygun_cli.dart -o raygun-cli` - Build executable - `dart run bin/raygun_cli.dart` - Run CLI locally - `dart format .` - Format code +- `dart run build_runner build` - Generate mocks (one-time) +- `dart run build_runner watch` - Auto-regenerate mocks on changes ## Architecture - **CLI Tool**: Uploads sourcemaps, manages obfuscation symbols, tracks deployments for Raygun.com - **Main Entry**: `bin/raygun_cli.dart` - CLI argument parsing and command routing -- **Commands**: `lib/src/` - Four main command modules: sourcemap, symbols, deployments, proguard +- **Commands**: `lib/src/` - Five main command modules: sourcemap, symbols, deployments, proguard, dsym - **APIs**: Each command has corresponding API client (`*_api.dart`) for Raygun REST API calls - **Config**: `config_props.dart` handles arg parsing with env var fallbacks (RAYGUN_APP_ID, RAYGUN_TOKEN, RAYGUN_API_KEY) +### Directory Structure +``` +lib/src/[command]/ + ├── [command]_command.dart # CLI parsing and routing + ├── [command]_api.dart # Raygun API integration + └── [command].dart # Business logic (if complex) +lib/src/core/ # Shared utilities (RaygunCommand, RaygunApi builders) +test/[command]/ # Tests mirror lib structure +``` + +### Dependency Flow +``` +bin/raygun_cli.dart → lib/src/[command]/[command]_command.dart → *_api.dart +``` +- Commands are stateless; API clients handle HTTP +- Commands receive API clients via constructor (dependency injection) + ## Code Style - **Imports**: Standard library first, then package imports, then relative imports - **Naming**: Snake_case for files/dirs, camelCase for variables, PascalCase for classes @@ -23,3 +42,308 @@ - **Types**: Use explicit types for public APIs, required named parameters preferred - **Strings**: Use single quotes, string interpolation with $variable or ${expression} - **Comments**: Use /// for public API documentation, avoid inline comments + +## Error Handling & Exit Codes +Standard error handling patterns used throughout: +- **Exit code 0**: Success +- **Exit code 1**: Failure (operation didn't succeed) +- **Exit code 2**: Error (exception or invalid input) +- Print errors to console before returning/exiting +- Use `Future` return type for async operations +- Chain with `.then()` and `.catchError()` for error handling + +Example: +```dart +run().then((result) { + if (result) { + exit(0); + } else { + exit(2); + } +}).catchError((e) { + print('Error: $e'); + exit(2); +}); +``` + +## Command Implementation Patterns +All commands extend the `RaygunCommand` abstract class: + +### Required Implementation +```dart +class MyCommand extends RaygunCommand { + const MyCommand({required this.api}); + + final MyApi api; + + @override + String get name => 'mycommand'; + + @override + ArgParser buildParser() { /* ... */ } + + @override + void execute(ArgResults command, bool verbose) { /* ... */ } +} +``` + +### Command Patterns +- **Help Flag**: Always check for `--help` flag first and exit(0) +- **Config Loading**: Use `ConfigProp.load()` for app-id, token, api-key (exits if missing) +- **Subcommands**: Use `ArgParser.addCommand()` (see symbols: upload/list/delete) +- **Verbose Flag**: Available in all commands (inherited from main parser) +- **Mandatory Args**: Use `mandatory: true` in ArgParser for required flags + +### Example: Subcommands Pattern (symbols command) +```dart +ArgParser buildParser() { + return ArgParser() + ..addOption('app-id') + ..addOption('token') + ..addCommand('upload') + ..addCommand('list') + ..addCommand('delete'); +} +``` + +## API Client Patterns +Each command has a corresponding API client: + +### Builder Pattern for Requests +- `RaygunMultipartRequestBuilder` - For file uploads +- `RaygunPostRequestBuilder` - For JSON POST requests + +Example: +```dart +final request = RaygunMultipartRequestBuilder(url, 'POST') + .addBearerToken(token) + .addFile('file', filePath) + .addField('version', version) + .build(); +``` + +### API Client Structure +- Factory method pattern: Use static `.create()` for production instances +- Return `Future` to indicate success/failure +- Print response codes and messages for debugging +- Handle HTTP responses and errors appropriately + +## Testing Patterns & Mock Generation + +### Test Structure +- Tests use `mockito` for mocking API clients +- Test files mirror `lib/` structure (e.g., `lib/src/symbols/` → `test/symbols/`) +- Mock files use `.mocks.dart` suffix and are git-tracked +- Generate mocks with: `dart run build_runner build` + +### Test Pattern +```dart +// 1. Generate mock classes with @GenerateMocks annotation +@GenerateMocks([MyApi]) +void main() { + group('MyCommand', () { + late MockMyApi mockApi; + + setUp(() { + mockApi = MockMyApi(); + }); + + test('description', () async { + // 2. Setup mock behavior + when(mockApi.someMethod()).thenAnswer((_) async => true); + + // 3. Inject mock into command + final command = MyCommand(api: mockApi); + + // 4. Execute and verify + final result = await command.run(...); + expect(result, true); + }); + }); +} +``` + +### Mock Regeneration +- After changing API signatures, run: `dart run build_runner build` +- Use `watch` mode during development: `dart run build_runner watch` + +## Dependency Injection + +### Pattern +- Commands receive API clients via constructor +- Global command instances use `.create()` factories +- Tests inject mock API clients +- Enables testing without hitting real APIs + +Example: +```dart +// Production usage (in command file) +SymbolsCommand symbolsCommand = SymbolsCommand(api: SymbolsApi.create()); + +// Test usage +final mockApi = MockSymbolsApi(); +final command = SymbolsCommand(api: mockApi); +``` + +## CI/CD & Release Workflow + +### PR Requirements +- **Title**: Must follow Conventional Commits (enforced by `.github/workflows/pr.yml`) +- **Checks**: All must pass - format, analyze, test +- **Platforms**: Multi-platform builds run automatically (Linux, macOS, Windows) + +### Conventional Commits Format +Examples: +- `feat: add new command for X` +- `fix: resolve issue with Y` +- `chore: update dependencies` +- `docs: update README` + +### Version Management +**IMPORTANT**: Update BOTH files when releasing: +1. `pubspec.yaml` - version field +2. `bin/raygun_cli.dart` - version constant + +### Workflows +- **pr.yml**: Validates PR title format +- **main.yml**: Runs tests, format check, analysis, and builds binaries for all platforms +- **release.yml**: On GitHub release, builds and uploads zipped binaries + +## Development Workflow + +### Local Testing +```bash +# Run CLI locally with arguments +dart run bin/raygun_cli.dart + +# Use verbose flag for debug output +dart run bin/raygun_cli.dart -v sourcemap --help + +# Set environment variables for testing +export RAYGUN_APP_ID=test-app-id +export RAYGUN_TOKEN=test-token +export RAYGUN_API_KEY=test-api-key +``` + +### Testing Workflow +1. Write/modify API client code +2. Add `@GenerateMocks([MyApi])` to test file +3. Run `dart run build_runner build` to generate mocks +4. Write tests using mock instances +5. Run `dart test` to verify + +### Build for Distribution +```bash +# Compile for current platform +dart compile exe bin/raygun_cli.dart -o raygun-cli + +# Note: Cross-compilation not supported; use CI for other platforms +``` + +## Common Gotchas & Best Practices + +### Validation Order +1. Always validate `--help` flag first before parsing other args +2. `ConfigProp.load()` calls `exit(2)` if required config is missing +3. Check mandatory args before executing business logic + +### File Operations +- Use `File.existsSync()` before file operations +- Throw descriptive exceptions if files don't exist +- Use `.split("/").last` to get filename from path + +### Argument Parsing +- Use `command.wasParsed('flag')` to check if flag was provided +- Use `command['option']` to get option value +- Use `command.command?.name` to get subcommand name + +### Async Patterns +- Prefer `.then()` and `.catchError()` over try/catch for CLI commands +- Always handle errors gracefully with user-friendly messages +- Use `Future` for operations that can succeed or fail + +### String Conventions +- Always use single quotes for strings (Dart convention) +- Use string interpolation: `'Value: $variable'` or `'Value: ${expression}'` + +## Quick Reference + +### Command Examples +```bash +# Sourcemap upload (Flutter) +dart run bin/raygun_cli.dart sourcemap -p flutter \ + --uri=https://example.com/main.dart.js \ + --app-id=XXX --token=YYY + +# Sourcemap upload (single file) +dart run bin/raygun_cli.dart sourcemap \ + --input-map=path/to/index.js.map \ + --uri=https://example.com/index.js \ + --app-id=XXX --token=YYY + +# Symbols upload +dart run bin/raygun_cli.dart symbols upload \ + --path=app.android-arm64.symbols \ + --version=1.0.0 \ + --app-id=XXX --token=YYY + +# Symbols list +dart run bin/raygun_cli.dart symbols list \ + --app-id=XXX --token=YYY + +# Symbols delete +dart run bin/raygun_cli.dart symbols delete \ + --id=2c7a3u3 \ + --app-id=XXX --token=YYY + +# Deployments tracking +dart run bin/raygun_cli.dart deployments \ + --version=1.0.0 \ + --token=YYY \ + --api-key=ZZZ \ + --scm-type=GitHub \ + --scm-identifier=abc123 + +# Proguard upload +dart run bin/raygun_cli.dart proguard \ + --app-id=XXX \ + --version=1.0.0 \ + --path=mapping.txt \ + --external-access-token=EAT \ + --overwrite + +# iOS dSYM upload +dart run bin/raygun_cli.dart dsym \ + --app-id=XXX \ + --path=path/to/dsym.zip \ + --external-access-token=EAT +``` + +### Environment Variables +```bash +export RAYGUN_APP_ID=your-app-id +export RAYGUN_TOKEN=your-token +export RAYGUN_API_KEY=your-api-key +``` + +## Known TODOs & Future Improvements +- Config file support (.raygun.yaml or similar) - see `lib/src/config_props.dart:9` +- NodeJS sourcemap platform support (currently stubbed in sourcemap command) +- System package manager installations (brew, apt, etc.) + +## Troubleshooting + +### Mock Generation Issues +- Ensure `@GenerateMocks` annotation is present in test file +- Run `dart pub get` to ensure dependencies are installed +- Clean and rebuild: `dart run build_runner clean && dart run build_runner build` + +### Build Issues +- Ensure Dart SDK version matches `pubspec.yaml` requirement (^3.5.0) +- Run `dart pub get` to update dependencies +- Check that version in `bin/raygun_cli.dart` matches `pubspec.yaml` + +### Test Failures +- Verify mocks are regenerated after API changes +- Check that mock behavior is properly stubbed with `when()` +- Ensure async tests use `async/await` or return Future diff --git a/README.md b/README.md index 782117b..f0a2bcf 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,44 @@ Response: ``` +#### iOS dSYM Uploader + +Upload dSYM files for iOS to [raygun.com](https://raygun.com). + +Documentation: https://raygun.com/documentation/language-guides/apple/crash-reporting/advanced-setup/#symbolication + +``` +raygun-cli dsym +``` + +Minimal required arguments are: + +``` +raygun-cli dsym --app-id=APP_ID --path= --external-access-token= +``` + +Example outputs: + +``` +Success: + +Uploading: /app.dSYM.zip +Success uploading dSYM file: 200 +Result: {"Status":"Success","Message":"dSYM files uploaded"} + +Wrong External Access Token: + +Uploading: /app.dSYM.zip +Error uploading dSYM file: 302 +Response: + +Invalid dSYM file: + +Uploading: /invalid.zip +Success uploading dSYM file: 200 +Result: {"Status":"Failure","Message":"No dSYM file found"} +``` + #### Flutter obfuscation symbols Manages obfuscation symbols to [raygun.com](https://raygun.com). diff --git a/bin/raygun_cli.dart b/bin/raygun_cli.dart index b643233..fc2c756 100644 --- a/bin/raygun_cli.dart +++ b/bin/raygun_cli.dart @@ -17,27 +17,12 @@ ArgParser buildParser() { negatable: false, help: 'Show additional command output.', ) - ..addFlag( - 'version', - negatable: false, - help: 'Print the tool version.', - ) - ..addCommand( - sourcemapCommand.name, - sourcemapCommand.buildParser(), - ) - ..addCommand( - symbolsCommand.name, - symbolsCommand.buildParser(), - ) - ..addCommand( - deploymentsCommand.name, - deploymentsCommand.buildParser(), - ) - ..addCommand( - proguardCommand.name, - proguardCommand.buildParser(), - ); + ..addFlag('version', negatable: false, help: 'Print the tool version.') + ..addCommand(sourcemapCommand.name, sourcemapCommand.buildParser()) + ..addCommand(symbolsCommand.name, symbolsCommand.buildParser()) + ..addCommand(deploymentsCommand.name, deploymentsCommand.buildParser()) + ..addCommand(proguardCommand.name, proguardCommand.buildParser()) + ..addCommand(dsymCommand.name, dsymCommand.buildParser()); } void printUsage(ArgParser argParser) { @@ -95,6 +80,11 @@ void main(List arguments) { return; } + if (results.command?.name == dsymCommand.name) { + dsymCommand.execute(results.command!, verbose); + return; + } + throw FormatException('Unknown or missing command.'); } on FormatException catch (e) { // Print usage information if an invalid argument was provided. diff --git a/lib/raygun_cli.dart b/lib/raygun_cli.dart index 91a86db..edb158a 100644 --- a/lib/raygun_cli.dart +++ b/lib/raygun_cli.dart @@ -1,4 +1,5 @@ export 'src/deployments/deployments_command.dart'; +export 'src/dsym/dsym_command.dart'; export 'src/proguard/proguard_command.dart'; export 'src/sourcemap/sourcemap_command.dart'; export 'src/symbols/symbols_command.dart'; diff --git a/lib/src/config_props.dart b/lib/src/config_props.dart index 430bcdc..29b1eb2 100644 --- a/lib/src/config_props.dart +++ b/lib/src/config_props.dart @@ -29,10 +29,7 @@ class ConfigProp { /// The environment variable key final String envKey; - const ConfigProp({ - required this.name, - required this.envKey, - }); + const ConfigProp({required this.name, required this.envKey}); /// Load the value of the property from arguments or environment variables String load(ArgResults arguments) { @@ -45,7 +42,8 @@ class ConfigProp { if (value == null) { print('Error: Missing "$name"'); print( - ' Please provide "$name" via argument or environment variable "$envKey"'); + ' Please provide "$name" via argument or environment variable "$envKey"', + ); exit(2); } return value; diff --git a/lib/src/deployments/deployments_api.dart b/lib/src/deployments/deployments_api.dart index c7073c0..49827fa 100644 --- a/lib/src/deployments/deployments_api.dart +++ b/lib/src/deployments/deployments_api.dart @@ -35,10 +35,9 @@ class DeploymentsApi { 'deployedAt': DateTime.now().toUtc().toIso8601String(), }; - final request = RaygunPostRequestBuilder(url) - .addBearerToken(token) - .addJsonBody(payload) - .build(); + final request = RaygunPostRequestBuilder( + url, + ).addBearerToken(token).addJsonBody(payload).build(); try { final response = await httpClient.send(request); @@ -47,7 +46,8 @@ class DeploymentsApi { if (response.statusCode == 200 || response.statusCode == 201) { print('Success creating deployment: ${response.statusCode}'); print( - 'Deployment identifier: ${jsonDecode(responseBody)['identifier']}'); + 'Deployment identifier: ${jsonDecode(responseBody)['identifier']}', + ); return true; } diff --git a/lib/src/deployments/deployments_command.dart b/lib/src/deployments/deployments_command.dart index 0856bc1..9155e48 100644 --- a/lib/src/deployments/deployments_command.dart +++ b/lib/src/deployments/deployments_command.dart @@ -5,8 +5,9 @@ import 'package:raygun_cli/src/core/raygun_command.dart'; import 'package:raygun_cli/src/deployments/deployments.dart'; import 'package:raygun_cli/src/deployments/deployments_api.dart'; -final DeploymentsCommand deploymentsCommand = - DeploymentsCommand(api: DeploymentsApi.create()); +final DeploymentsCommand deploymentsCommand = DeploymentsCommand( + api: DeploymentsApi.create(), +); class DeploymentsCommand extends RaygunCommand { const DeploymentsCommand({required this.api}); @@ -25,11 +26,7 @@ class DeploymentsCommand extends RaygunCommand { negatable: false, help: 'Print deployments usage information', ) - ..addOption( - 'token', - mandatory: true, - help: 'Raygun access token', - ) + ..addOption('token', mandatory: true, help: 'Raygun access token') ..addOption( 'api-key', mandatory: true, @@ -63,11 +60,7 @@ class DeploymentsCommand extends RaygunCommand { mandatory: false, help: 'Email address of the person deploying the software', ) - ..addOption( - 'comment', - mandatory: false, - help: 'Deployment comment', - ); + ..addOption('comment', mandatory: false, help: 'Deployment comment'); } @override @@ -78,19 +71,18 @@ class DeploymentsCommand extends RaygunCommand { exit(0); } - Deployments( - command: command, - verbose: verbose, - deploymentsApi: api, - ).notify().then((success) { - if (success) { - exit(0); - } else { - exit(1); - } - }).catchError((error) { - print('Error creating deployment: $error'); - exit(2); - }); + Deployments(command: command, verbose: verbose, deploymentsApi: api) + .notify() + .then((success) { + if (success) { + exit(0); + } else { + exit(1); + } + }) + .catchError((error) { + print('Error creating deployment: $error'); + exit(2); + }); } } diff --git a/lib/src/dsym/dsym.dart b/lib/src/dsym/dsym.dart new file mode 100644 index 0000000..5ee53e8 --- /dev/null +++ b/lib/src/dsym/dsym.dart @@ -0,0 +1,44 @@ +import 'package:raygun_cli/src/dsym/dsym_api.dart'; +import 'package:args/args.dart'; +import '../config_props.dart'; + +class Dsym { + final ArgResults command; + final bool verbose; + final DsymApi api; + + Dsym({required this.command, required this.verbose, required this.api}); + + Future upload() async { + if (!command.wasParsed('path')) { + print('Error: Missing "--path"'); + print(' Please provide "--path" via argument'); + return false; + } + + if (!command.wasParsed('external-access-token')) { + print('Error: Missing "--external-access-token"'); + print(' Please provide "--external-access-token" via argument'); + return false; + } + + final externalAccessToken = + command.option('external-access-token') as String; + final path = command.option('path') as String; + final appId = ConfigProp.appId.load(command); + + if (verbose) { + print('app-id: $appId'); + print('external-access-token: $externalAccessToken'); + print('path: $path'); + } + + final success = await api.uploadDsym( + appId: appId, + externalAccessToken: externalAccessToken, + path: path, + ); + + return success; + } +} diff --git a/lib/src/dsym/dsym_api.dart b/lib/src/dsym/dsym_api.dart new file mode 100644 index 0000000..6c75dc4 --- /dev/null +++ b/lib/src/dsym/dsym_api.dart @@ -0,0 +1,50 @@ +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:raygun_cli/src/core/raygun_api.dart'; + +class DsymApi { + const DsymApi(this.httpClient); + + final http.Client httpClient; + + DsymApi.create() : httpClient = http.Client(); + + /// Uploads a dSYM zip file to Raygun. + /// Returns true if the upload was successful. + Future uploadDsym({ + required String appId, + required String externalAccessToken, + required String path, + }) async { + final file = File(path); + if (!file.existsSync()) { + print('$path: file not found!'); + return false; + } + print('Uploading: $path'); + + final request = RaygunMultipartRequestBuilder( + 'https://app.raygun.com/dashboard/$appId/settings/symbols?authToken=$externalAccessToken', + 'POST', + ).addFile('DsymFile', path).build(); + + try { + final response = await httpClient.send(request); + final responseBody = await response.stream.bytesToString(); + + if (response.statusCode == 200) { + print('Success uploading dSYM file: ${response.statusCode}'); + print('Result: $responseBody'); + return true; + } + + print('Error uploading dSYM file: ${response.statusCode}'); + print('Response: $responseBody'); + return false; + } catch (e) { + print('Exception while uploading dSYM file: $e'); + return false; + } + } +} diff --git a/lib/src/dsym/dsym_command.dart b/lib/src/dsym/dsym_command.dart new file mode 100644 index 0000000..5ba5d44 --- /dev/null +++ b/lib/src/dsym/dsym_command.dart @@ -0,0 +1,58 @@ +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:raygun_cli/src/core/raygun_command.dart'; +import 'package:raygun_cli/src/dsym/dsym.dart'; +import 'package:raygun_cli/src/dsym/dsym_api.dart'; + +final DsymCommand dsymCommand = DsymCommand(api: DsymApi.create()); + +class DsymCommand extends RaygunCommand { + const DsymCommand({required this.api}); + + final DsymApi api; + + @override + String get name => 'dsym'; + + @override + ArgParser buildParser() { + return ArgParser() + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Print dsym usage information', + ) + ..addOption('app-id', mandatory: true, help: 'Raygun application ID') + ..addOption( + 'external-access-token', + mandatory: true, + help: 'Raygun external access token', + ) + ..addOption('path', mandatory: true, help: 'Path to the dSYM zip file'); + } + + @override + void execute(ArgResults command, bool verbose) { + if (command.wasParsed('help')) { + print('Usage: raygun-cli $name '); + print(buildParser().usage); + exit(0); + } + + Dsym(command: command, verbose: verbose, api: api) + .upload() + .then((success) { + if (success) { + exit(0); + } else { + exit(1); + } + }) + .catchError((error) { + print('Error uploading dSYM file: $error'); + exit(2); + }); + } +} diff --git a/lib/src/proguard/proguard.dart b/lib/src/proguard/proguard.dart index 3fc88f5..1a9ce51 100644 --- a/lib/src/proguard/proguard.dart +++ b/lib/src/proguard/proguard.dart @@ -7,11 +7,7 @@ class Proguard { final bool verbose; final ProguardApi api; - Proguard({ - required this.command, - required this.verbose, - required this.api, - }); + Proguard({required this.command, required this.verbose, required this.api}); Future upload() async { if (!command.wasParsed('path')) { diff --git a/lib/src/proguard/proguard_api.dart b/lib/src/proguard/proguard_api.dart index 3478414..c9f6dff 100644 --- a/lib/src/proguard/proguard_api.dart +++ b/lib/src/proguard/proguard_api.dart @@ -43,7 +43,8 @@ class ProguardApi { if (response.statusCode == 200) { print( - 'Success uploading Proguard/R8 mapping file: ${response.statusCode}'); + 'Success uploading Proguard/R8 mapping file: ${response.statusCode}', + ); print('Result: $responseBody'); return true; } diff --git a/lib/src/proguard/proguard_command.dart b/lib/src/proguard/proguard_command.dart index b456513..43e58b7 100644 --- a/lib/src/proguard/proguard_command.dart +++ b/lib/src/proguard/proguard_command.dart @@ -5,8 +5,9 @@ import 'package:raygun_cli/src/core/raygun_command.dart'; import 'package:raygun_cli/src/proguard/proguard.dart'; import 'package:raygun_cli/src/proguard/proguard_api.dart'; -final ProguardCommand proguardCommand = - ProguardCommand(api: ProguardApi.create()); +final ProguardCommand proguardCommand = ProguardCommand( + api: ProguardApi.create(), +); class ProguardCommand extends RaygunCommand { const ProguardCommand({required this.api}); @@ -25,11 +26,7 @@ class ProguardCommand extends RaygunCommand { negatable: false, help: 'Print proguard usage information', ) - ..addOption( - 'app-id', - mandatory: true, - help: 'Raygun application ID', - ) + ..addOption('app-id', mandatory: true, help: 'Raygun application ID') ..addOption( 'external-access-token', mandatory: true, @@ -60,19 +57,18 @@ class ProguardCommand extends RaygunCommand { exit(0); } - Proguard( - command: command, - verbose: verbose, - api: api, - ).upload().then((success) { - if (success) { - exit(0); - } else { - exit(1); - } - }).catchError((error) { - print('Error uploading ProGuard mapping file: $error'); - exit(2); - }); + Proguard(command: command, verbose: verbose, api: api) + .upload() + .then((success) { + if (success) { + exit(0); + } else { + exit(1); + } + }) + .catchError((error) { + print('Error uploading ProGuard mapping file: $error'); + exit(2); + }); } } diff --git a/lib/src/sourcemap/sourcemap_api.dart b/lib/src/sourcemap/sourcemap_api.dart index 5e05925..0dcdee4 100644 --- a/lib/src/sourcemap/sourcemap_api.dart +++ b/lib/src/sourcemap/sourcemap_api.dart @@ -3,9 +3,7 @@ import 'package:http/http.dart' as http; import 'package:raygun_cli/src/core/raygun_api.dart'; class SourcemapApi { - const SourcemapApi({ - required this.httpClient, - }); + const SourcemapApi({required this.httpClient}); /// Creates a new instance of [SourcemapApi] with the provided [httpClient]. SourcemapApi.create() : httpClient = http.Client(); @@ -32,12 +30,21 @@ class SourcemapApi { 'PUT', ).addBearerToken(token).addField('uri', uri).addFile('file', path).build(); - final response = await httpClient.send(request); - if (response.statusCode == 200) { - print('File uploaded successfully!'); - return true; - } else { + try { + final response = await httpClient.send(request); + final responseBody = await response.stream.bytesToString(); + + if (response.statusCode == 200) { + print('File uploaded successfully!'); + print('Result: $responseBody'); + return true; + } + print('Error uploading file. Response code: ${response.statusCode}'); + print('Response: $responseBody'); + return false; + } catch (e) { + print('Exception while uploading sourcemap file: $e'); return false; } } diff --git a/lib/src/sourcemap/sourcemap_command.dart b/lib/src/sourcemap/sourcemap_command.dart index 75e10f6..b8b70fe 100644 --- a/lib/src/sourcemap/sourcemap_command.dart +++ b/lib/src/sourcemap/sourcemap_command.dart @@ -7,13 +7,12 @@ import 'package:raygun_cli/src/sourcemap/node/sourcemap_node.dart'; import 'package:raygun_cli/src/sourcemap/sourcemap_api.dart'; import 'package:raygun_cli/src/sourcemap/sourcemap_single_file.dart'; -SourcemapCommand sourcemapCommand = - SourcemapCommand(api: SourcemapApi.create()); +SourcemapCommand sourcemapCommand = SourcemapCommand( + api: SourcemapApi.create(), +); class SourcemapCommand extends RaygunCommand { - const SourcemapCommand({ - required this.api, - }); + const SourcemapCommand({required this.api}); final SourcemapApi api; @@ -29,14 +28,8 @@ class SourcemapCommand extends RaygunCommand { negatable: false, help: 'Print sourcemap usage information.', ) - ..addOption( - 'app-id', - help: 'Raygun application ID', - ) - ..addOption( - 'token', - help: 'Raygun access token', - ) + ..addOption('app-id', help: 'Raygun application ID') + ..addOption('token', help: 'Raygun access token') ..addOption( 'platform', abbr: 'p', @@ -50,48 +43,33 @@ class SourcemapCommand extends RaygunCommand { 'base-uri', help: 'Base application URI (e.g. https://localhost:3000/)', ) - ..addOption( - 'input-map', - abbr: 'i', - help: 'Single sourcemap file', - ) - ..addOption( - 'src', - abbr: 's', - help: 'Source files', - ); + ..addOption('input-map', abbr: 'i', help: 'Single sourcemap file') + ..addOption('src', abbr: 's', help: 'Source files'); } @override - void execute( - ArgResults command, - bool verbose, - ) { + void execute(ArgResults command, bool verbose) { if (command.wasParsed('help')) { print('Usage: raygun-cli $name (upload|list|delete) '); print(buildParser().usage); exit(0); } - run( - command: command, - verbose: verbose, - ).then((result) { - if (result) { - exit(0); - } else { - exit(2); - } - }).catchError((e) { - print('Error: $e'); - exit(2); - }); + run(command: command, verbose: verbose) + .then((result) { + if (result) { + exit(0); + } else { + exit(2); + } + }) + .catchError((e) { + print('Error: $e'); + exit(2); + }); } - Future run({ - required ArgResults command, - required bool verbose, - }) async { + Future run({required ArgResults command, required bool verbose}) async { if (command.wasParsed('help')) { print('Usage: raygun-cli sourcemap '); print(buildParser().usage); diff --git a/lib/src/symbols/symbols_api.dart b/lib/src/symbols/symbols_api.dart index b31e0ae..693800d 100644 --- a/lib/src/symbols/symbols_api.dart +++ b/lib/src/symbols/symbols_api.dart @@ -3,9 +3,7 @@ import 'package:http/http.dart' as http; import 'package:raygun_cli/src/core/raygun_api.dart'; class SymbolsApi { - const SymbolsApi({ - required this.httpClient, - }); + const SymbolsApi({required this.httpClient}); /// Creates a new instance of [SymbolsApi] with the provided [httpClient]. SymbolsApi.create() : httpClient = http.Client(); @@ -19,14 +17,15 @@ class SymbolsApi { required String path, required String version, }) async { - final request = RaygunMultipartRequestBuilder( - 'https://api.raygun.com/v3/applications/$appId/flutter-symbols', - 'PUT', - ) - .addBearerToken(token) - .addField('version', version) - .addFile('file', path) - .build(); + final request = + RaygunMultipartRequestBuilder( + 'https://api.raygun.com/v3/applications/$appId/flutter-symbols', + 'PUT', + ) + .addBearerToken(token) + .addField('version', version) + .addFile('file', path) + .build(); final response = await httpClient.send(request); if (response.statusCode == 200 || response.statusCode == 201) { diff --git a/lib/src/symbols/symbols_command.dart b/lib/src/symbols/symbols_command.dart index b08d3b1..da10080 100644 --- a/lib/src/symbols/symbols_command.dart +++ b/lib/src/symbols/symbols_command.dart @@ -8,9 +8,7 @@ import 'package:raygun_cli/src/symbols/symbols_api.dart'; SymbolsCommand symbolsCommand = SymbolsCommand(api: SymbolsApi.create()); class SymbolsCommand extends RaygunCommand { - const SymbolsCommand({ - required this.api, - }); + const SymbolsCommand({required this.api}); final SymbolsApi api; @@ -25,19 +23,21 @@ class SymbolsCommand extends RaygunCommand { exit(0); } run( - command: command, - appId: ConfigProp.appId.load(command), - token: ConfigProp.token.load(command), - ).then((result) { - if (result) { - exit(0); - } else { - exit(2); - } - }).catchError((e) { - print('Error: $e'); - exit(2); - }); + command: command, + appId: ConfigProp.appId.load(command), + token: ConfigProp.token.load(command), + ) + .then((result) { + if (result) { + exit(0); + } else { + exit(2); + } + }) + .catchError((e) { + print('Error: $e'); + exit(2); + }); } Future run({ @@ -62,10 +62,7 @@ class SymbolsCommand extends RaygunCommand { } if (command.command?.name == 'list') { - return api.listSymbols( - appId: appId, - token: token, - ); + return api.listSymbols(appId: appId, token: token); } if (command.command?.name == 'delete') { @@ -74,11 +71,7 @@ class SymbolsCommand extends RaygunCommand { print(buildParser().usage); return false; } - return api.deleteSymbols( - appId: appId, - token: token, - id: command['id'], - ); + return api.deleteSymbols(appId: appId, token: token, id: command['id']); } return false; @@ -93,34 +86,13 @@ class SymbolsCommand extends RaygunCommand { negatable: false, help: 'Print $name usage information.', ) - ..addOption( - 'app-id', - help: 'Raygun application ID', - ) - ..addOption( - 'token', - help: 'Raygun access token', - ) - ..addOption( - 'path', - help: 'Path to symbols file, used in upload', - ) - ..addOption( - 'version', - help: 'App version, used in upload', - ) - ..addOption( - 'id', - help: 'Symbol ID, used in delete', - ) - ..addCommand( - 'upload', - ) - ..addCommand( - 'list', - ) - ..addCommand( - 'delete', - ); + ..addOption('app-id', help: 'Raygun application ID') + ..addOption('token', help: 'Raygun access token') + ..addOption('path', help: 'Path to symbols file, used in upload') + ..addOption('version', help: 'App version, used in upload') + ..addOption('id', help: 'Symbol ID, used in delete') + ..addCommand('upload') + ..addCommand('list') + ..addCommand('delete'); } } diff --git a/pubspec.lock b/pubspec.lock index 635c102..c3e1a65 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: dd3d2ad434b9510001d089e8de7556d50c834481b9abc2891a0184a8493a19dc + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "89.0.0" + version: "92.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: c22b6e7726d1f9e5db58c7251606076a71ca0dbcf76116675edfadbec0c9e875 + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" url: "https://pub.dev" source: hosted - version: "8.2.0" + version: "9.0.0" args: dependency: "direct main" description: @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" boolean_selector: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: build - sha256: "5b887c55a0f734b433b3b2d89f9cd1f99eb636b17e268a5b4259258bc916504b" + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.3" build_config: dependency: transitive description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: a9461b8e586bf018dd4afd2e13b49b08c6a844a4b226c8d1d10f3a723cdd78c3 + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.10.4" built_collection: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: built_value - sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" url: "https://pub.dev" source: hosted - version: "8.10.1" + version: "8.12.1" checked_yaml: dependency: transitive description: @@ -97,14 +97,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" code_builder: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.11.0" collection: dependency: transitive description: @@ -125,26 +133,26 @@ packages: dependency: transitive description: name: coverage - sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.15.0" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" dart_style: dependency: transitive description: name: dart_style - sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" file: dependency: transitive description: @@ -189,10 +197,10 @@ packages: dependency: "direct main" description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -217,14 +225,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf - url: "https://pub.dev" - source: hosted - version: "0.7.1" json_annotation: dependency: transitive description: @@ -253,18 +253,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -277,10 +277,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "4feb43bc4eb6c03e832f5fcd637d1abb44b98f9cfa245c58e27382f58859f8f6" + sha256: dac24d461418d363778d53198d9ac0510b9d073869f078450f195766ec48d05e url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.6.1" node_preamble: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: package_config - sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.0" path: dependency: transitive description: @@ -309,18 +309,18 @@ packages: dependency: transitive description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.5.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" pubspec_parse: dependency: transitive description: @@ -357,18 +357,18 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" source_gen: dependency: transitive description: name: source_gen - sha256: ccf30b0c9fbcd79d8b6f5bfac23199fb354938436f62475e14aea0f29ee0f800 + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.1.1" source_map_stack_trace: dependency: transitive description: @@ -437,26 +437,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae" url: "https://pub.dev" source: hosted - version: "1.26.3" + version: "1.28.0" test_api: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.8" test_core: dependency: transitive description: name: test_core - sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4 url: "https://pub.dev" source: hosted - version: "0.6.12" + version: "0.6.14" typed_data: dependency: transitive description: @@ -469,42 +469,42 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.4" web: dependency: transitive description: name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web_socket: dependency: transitive description: name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.1.6" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: @@ -522,4 +522,4 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" + dart: ">=3.10.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7797de0..992c517 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,14 +10,14 @@ executables: raygun-cli: 'raygun_cli' environment: - sdk: ^3.5.0 + sdk: ^3.10.0 dependencies: args: ^2.6.0 - http: ^1.3.0 + http: ^1.6.0 dev_dependencies: lints: ">=4.0.0 <7.0.0" - test: ^1.24.0 - mockito: ^5.4.0 - build_runner: ^2.4.0 + test: ^1.28.0 + mockito: ^5.6.1 + build_runner: ^2.10.4 diff --git a/test/config_props_test.dart b/test/config_props_test.dart index 2e6bb31..388ecd9 100644 --- a/test/config_props_test.dart +++ b/test/config_props_test.dart @@ -10,8 +10,10 @@ void main() { ..addFlag('verbose') ..addOption('app-id') ..addOption('token'); - final results = - parser.parse(['--app-id=app-id-parsed', '--token=token-parsed']); + final results = parser.parse([ + '--app-id=app-id-parsed', + '--token=token-parsed', + ]); final token = ConfigProp.token.load(results); final appId = ConfigProp.appId.load(results); expect(appId, 'app-id-parsed'); diff --git a/test/deployments/deployments_api_test.dart b/test/deployments/deployments_api_test.dart index d2608d5..00efb71 100644 --- a/test/deployments/deployments_api_test.dart +++ b/test/deployments/deployments_api_test.dart @@ -20,25 +20,27 @@ void main() { }); group('createDeployment', () { - test('returns true when deployment creation is successful (201)', - () async { - final responseBody = jsonEncode({'identifier': 'test-deployment-id'}); - final response = http.StreamedResponse( - Stream.value(utf8.encode(responseBody)), - 201, - ); - - when(mockClient.send(any)).thenAnswer((_) async => response); - - final result = await deploymentsApi.createDeployment( - token: 'test-token', - apiKey: 'test-api-key', - version: '1.0.0', - ); - - expect(result, true); - verify(mockClient.send(any)).called(1); - }); + test( + 'returns true when deployment creation is successful (201)', + () async { + final responseBody = jsonEncode({'identifier': 'test-deployment-id'}); + final response = http.StreamedResponse( + Stream.value(utf8.encode(responseBody)), + 201, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await deploymentsApi.createDeployment( + token: 'test-token', + apiKey: 'test-api-key', + version: '1.0.0', + ); + + expect(result, true); + verify(mockClient.send(any)).called(1); + }, + ); test('returns true with all optional parameters', () async { final responseBody = jsonEncode({'identifier': 'test-deployment-id'}); diff --git a/test/deployments/deployments_api_test.mocks.dart b/test/deployments/deployments_api_test.mocks.dart index 50e84fb..015ffca 100644 --- a/test/deployments/deployments_api_test.mocks.dart +++ b/test/deployments/deployments_api_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/deployments/deployments_api_test.dart. // Do not manually edit this file. @@ -24,16 +24,17 @@ import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { _FakeResponse_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { _FakeStreamedResponse_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [Client]. @@ -47,26 +48,28 @@ class MockClient extends _i1.Mock implements _i2.Client { @override _i3.Future<_i2.Response> head(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#head, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#head, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#head, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> get(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#get, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#get, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#get, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> post( @@ -76,22 +79,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #post, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #post, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> put( @@ -101,22 +105,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #put, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #put, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> patch( @@ -126,22 +131,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #patch, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #patch, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> delete( @@ -151,34 +157,36 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #delete, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future read(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#read, [url], {#headers: headers}), - returnValue: _i3.Future.value( - _i5.dummyValue( - this, Invocation.method(#read, [url], {#headers: headers}), - ), - ), - ) as _i3.Future); + returnValue: _i3.Future.value( + _i5.dummyValue( + this, + Invocation.method(#read, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future); @override _i3.Future<_i6.Uint8List> readBytes( @@ -186,25 +194,27 @@ class MockClient extends _i1.Mock implements _i2.Client { Map? headers, }) => (super.noSuchMethod( - Invocation.method(#readBytes, [url], {#headers: headers}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) as _i3.Future<_i6.Uint8List>); + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) + as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( - Invocation.method(#send, [request]), - returnValue: _i3.Future<_i2.StreamedResponse>.value( - _FakeStreamedResponse_1( - this, Invocation.method(#send, [request]), - ), - ), - ) as _i3.Future<_i2.StreamedResponse>); + returnValue: _i3.Future<_i2.StreamedResponse>.value( + _FakeStreamedResponse_1( + this, + Invocation.method(#send, [request]), + ), + ), + ) + as _i3.Future<_i2.StreamedResponse>); @override void close() => super.noSuchMethod( - Invocation.method(#close, []), - returnValueForMissingStub: null, - ); + Invocation.method(#close, []), + returnValueForMissingStub: null, + ); } diff --git a/test/deployments/deployments_command_test.dart b/test/deployments/deployments_command_test.dart index 0bb3c98..f576e3f 100644 --- a/test/deployments/deployments_command_test.dart +++ b/test/deployments/deployments_command_test.dart @@ -21,16 +21,18 @@ void main() { group('notify', () { test('notify calls createDeployment with correct parameters', () async { - when(mockApi.createDeployment( - token: anyNamed('token'), - apiKey: anyNamed('apiKey'), - version: anyNamed('version'), - ownerName: anyNamed('ownerName'), - emailAddress: anyNamed('emailAddress'), - comment: anyNamed('comment'), - scmIdentifier: anyNamed('scmIdentifier'), - scmType: anyNamed('scmType'), - )).thenAnswer((_) async => true); + when( + mockApi.createDeployment( + token: anyNamed('token'), + apiKey: anyNamed('apiKey'), + version: anyNamed('version'), + ownerName: anyNamed('ownerName'), + emailAddress: anyNamed('emailAddress'), + comment: anyNamed('comment'), + scmIdentifier: anyNamed('scmIdentifier'), + scmType: anyNamed('scmType'), + ), + ).thenAnswer((_) async => true); final parser = command.buildParser(); final results = parser.parse([ @@ -53,58 +55,66 @@ void main() { final success = await deployments.notify(); expect(success, true); - verify(mockApi.createDeployment( - token: 'test-token', - apiKey: 'test-api-key', - version: '1.0.0', - ownerName: 'John Doe', - emailAddress: 'john@example.com', - comment: 'Test deployment', - scmIdentifier: 'abc123', - scmType: 'GitHub', - )).called(1); + verify( + mockApi.createDeployment( + token: 'test-token', + apiKey: 'test-api-key', + version: '1.0.0', + ownerName: 'John Doe', + emailAddress: 'john@example.com', + comment: 'Test deployment', + scmIdentifier: 'abc123', + scmType: 'GitHub', + ), + ).called(1); }); - test('notify calls createDeployment with only required parameters', - () async { - when(mockApi.createDeployment( - token: anyNamed('token'), - apiKey: anyNamed('apiKey'), - version: anyNamed('version'), - ownerName: anyNamed('ownerName'), - emailAddress: anyNamed('emailAddress'), - comment: anyNamed('comment'), - scmIdentifier: anyNamed('scmIdentifier'), - scmType: anyNamed('scmType'), - )).thenAnswer((_) async => true); - - final parser = command.buildParser(); - final results = parser.parse([ - '--token=test-token', - '--api-key=test-api-key', - '--version=1.0.0', - ]); - - deployments = Deployments( - command: results, - verbose: false, - deploymentsApi: mockApi, - ); - - final success = await deployments.notify(); - - expect(success, true); - verify(mockApi.createDeployment( - token: 'test-token', - apiKey: 'test-api-key', - version: '1.0.0', - ownerName: null, - emailAddress: null, - comment: null, - scmIdentifier: null, - scmType: null, - )).called(1); - }); + test( + 'notify calls createDeployment with only required parameters', + () async { + when( + mockApi.createDeployment( + token: anyNamed('token'), + apiKey: anyNamed('apiKey'), + version: anyNamed('version'), + ownerName: anyNamed('ownerName'), + emailAddress: anyNamed('emailAddress'), + comment: anyNamed('comment'), + scmIdentifier: anyNamed('scmIdentifier'), + scmType: anyNamed('scmType'), + ), + ).thenAnswer((_) async => true); + + final parser = command.buildParser(); + final results = parser.parse([ + '--token=test-token', + '--api-key=test-api-key', + '--version=1.0.0', + ]); + + deployments = Deployments( + command: results, + verbose: false, + deploymentsApi: mockApi, + ); + + final success = await deployments.notify(); + + expect(success, true); + verify( + mockApi.createDeployment( + token: 'test-token', + apiKey: 'test-api-key', + version: '1.0.0', + ownerName: null, + emailAddress: null, + comment: null, + scmIdentifier: null, + scmType: null, + ), + ).called(1); + }, + ); test('notify returns false when version is missing', () async { final parser = command.buildParser(); @@ -122,29 +132,33 @@ void main() { final success = await deployments.notify(); expect(success, false); - verifyNever(mockApi.createDeployment( - token: anyNamed('token'), - apiKey: anyNamed('apiKey'), - version: anyNamed('version'), - ownerName: anyNamed('ownerName'), - emailAddress: anyNamed('emailAddress'), - comment: anyNamed('comment'), - scmIdentifier: anyNamed('scmIdentifier'), - scmType: anyNamed('scmType'), - )); + verifyNever( + mockApi.createDeployment( + token: anyNamed('token'), + apiKey: anyNamed('apiKey'), + version: anyNamed('version'), + ownerName: anyNamed('ownerName'), + emailAddress: anyNamed('emailAddress'), + comment: anyNamed('comment'), + scmIdentifier: anyNamed('scmIdentifier'), + scmType: anyNamed('scmType'), + ), + ); }); test('notify returns false when API call fails', () async { - when(mockApi.createDeployment( - token: anyNamed('token'), - apiKey: anyNamed('apiKey'), - version: anyNamed('version'), - ownerName: anyNamed('ownerName'), - emailAddress: anyNamed('emailAddress'), - comment: anyNamed('comment'), - scmIdentifier: anyNamed('scmIdentifier'), - scmType: anyNamed('scmType'), - )).thenAnswer((_) async => false); + when( + mockApi.createDeployment( + token: anyNamed('token'), + apiKey: anyNamed('apiKey'), + version: anyNamed('version'), + ownerName: anyNamed('ownerName'), + emailAddress: anyNamed('emailAddress'), + comment: anyNamed('comment'), + scmIdentifier: anyNamed('scmIdentifier'), + scmType: anyNamed('scmType'), + ), + ).thenAnswer((_) async => false); final parser = command.buildParser(); final results = parser.parse([ @@ -162,16 +176,18 @@ void main() { final success = await deployments.notify(); expect(success, false); - verify(mockApi.createDeployment( - token: 'test-token', - apiKey: 'test-api-key', - version: '1.0.0', - ownerName: null, - emailAddress: null, - comment: null, - scmIdentifier: null, - scmType: null, - )).called(1); + verify( + mockApi.createDeployment( + token: 'test-token', + apiKey: 'test-api-key', + version: '1.0.0', + ownerName: null, + emailAddress: null, + comment: null, + scmIdentifier: null, + scmType: null, + ), + ).called(1); }); }); }); diff --git a/test/deployments/deployments_command_test.mocks.dart b/test/deployments/deployments_command_test.mocks.dart index a15a68a..82cf33d 100644 --- a/test/deployments/deployments_command_test.mocks.dart +++ b/test/deployments/deployments_command_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/deployments/deployments_command_test.dart. // Do not manually edit this file. @@ -22,10 +22,11 @@ import 'package:raygun_cli/src/deployments/deployments_api.dart' as _i3; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { _FakeClient_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [DeploymentsApi]. @@ -37,10 +38,12 @@ class MockDeploymentsApi extends _i1.Mock implements _i3.DeploymentsApi { } @override - _i2.Client get httpClient => (super.noSuchMethod( - Invocation.getter(#httpClient), - returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), - ) as _i2.Client); + _i2.Client get httpClient => + (super.noSuchMethod( + Invocation.getter(#httpClient), + returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), + ) + as _i2.Client); @override _i4.Future createDeployment({ @@ -54,16 +57,17 @@ class MockDeploymentsApi extends _i1.Mock implements _i3.DeploymentsApi { String? scmType, }) => (super.noSuchMethod( - Invocation.method(#createDeployment, [], { - #token: token, - #apiKey: apiKey, - #version: version, - #ownerName: ownerName, - #emailAddress: emailAddress, - #comment: comment, - #scmIdentifier: scmIdentifier, - #scmType: scmType, - }), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#createDeployment, [], { + #token: token, + #apiKey: apiKey, + #version: version, + #ownerName: ownerName, + #emailAddress: emailAddress, + #comment: comment, + #scmIdentifier: scmIdentifier, + #scmType: scmType, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); } diff --git a/test/dsym/dsym_api_test.dart b/test/dsym/dsym_api_test.dart new file mode 100644 index 0000000..adfef81 --- /dev/null +++ b/test/dsym/dsym_api_test.dart @@ -0,0 +1,144 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:raygun_cli/src/dsym/dsym_api.dart'; +import 'package:test/test.dart'; + +import 'dsym_api_test.mocks.dart'; + +@GenerateMocks([http.Client]) +void main() { + group('DsymApi', () { + late MockClient mockClient; + late DsymApi dsymApi; + + setUp(() { + mockClient = MockClient(); + dsymApi = DsymApi(mockClient); + }); + + group('uploadDsym', () { + test('returns true when upload is successful (200)', () async { + // Create a temporary test file + final testFile = File('test_dsym.zip'); + testFile.writeAsStringSync('test dsym content'); + + final response = http.StreamedResponse( + Stream.value(utf8.encode('Upload successful')), + 200, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test_dsym.zip', + ); + + expect(result, true); + verify(mockClient.send(any)).called(1); + + // Clean up + testFile.deleteSync(); + }); + + test('returns false when upload fails', () async { + final testFile = File('test_dsym.zip'); + testFile.writeAsStringSync('test dsym content'); + + final response = http.StreamedResponse( + Stream.value(utf8.encode('Bad request')), + 400, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test_dsym.zip', + ); + + expect(result, false); + + testFile.deleteSync(); + }); + + test('returns false when file does not exist', () async { + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'nonexistent_file.zip', + ); + + expect(result, false); + verifyNever(mockClient.send(any)); + }); + + test('handles network exceptions and returns false', () async { + final testFile = File('test_dsym.zip'); + testFile.writeAsStringSync('test dsym content'); + + when(mockClient.send(any)).thenThrow(Exception('Network error')); + + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test_dsym.zip', + ); + + expect(result, false); + + testFile.deleteSync(); + }); + + test('returns false for 401 unauthorized', () async { + final testFile = File('test_dsym.zip'); + testFile.writeAsStringSync('test dsym content'); + + final response = http.StreamedResponse( + Stream.value(utf8.encode('Unauthorized')), + 401, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'invalid-token', + path: 'test_dsym.zip', + ); + + expect(result, false); + + testFile.deleteSync(); + }); + + test('returns false for 500 internal server error', () async { + final testFile = File('test_dsym.zip'); + testFile.writeAsStringSync('test dsym content'); + + final response = http.StreamedResponse( + Stream.value(utf8.encode('Internal server error')), + 500, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await dsymApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test_dsym.zip', + ); + + expect(result, false); + + testFile.deleteSync(); + }); + }); + }); +} diff --git a/test/dsym/dsym_api_test.mocks.dart b/test/dsym/dsym_api_test.mocks.dart new file mode 100644 index 0000000..bdd3a31 --- /dev/null +++ b/test/dsym/dsym_api_test.mocks.dart @@ -0,0 +1,220 @@ +// Mocks generated by Mockito 5.4.6 from annotations +// in raygun_cli/test/dsym/dsym_api_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; +import 'dart:convert' as _i4; +import 'dart:typed_data' as _i6; + +import 'package:http/http.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i5; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member + +class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { + _FakeResponse_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeStreamedResponse_1 extends _i1.SmartFake + implements _i2.StreamedResponse { + _FakeStreamedResponse_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [Client]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClient extends _i1.Mock implements _i2.Client { + MockClient() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i2.Response> head(Uri? url, {Map? headers}) => + (super.noSuchMethod( + Invocation.method(#head, [url], {#headers: headers}), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#head, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> get(Uri? url, {Map? headers}) => + (super.noSuchMethod( + Invocation.method(#get, [url], {#headers: headers}), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#get, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> post( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> put( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> patch( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> delete( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); + + @override + _i3.Future read(Uri? url, {Map? headers}) => + (super.noSuchMethod( + Invocation.method(#read, [url], {#headers: headers}), + returnValue: _i3.Future.value( + _i5.dummyValue( + this, + Invocation.method(#read, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future); + + @override + _i3.Future<_i6.Uint8List> readBytes( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) + as _i3.Future<_i6.Uint8List>); + + @override + _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => + (super.noSuchMethod( + Invocation.method(#send, [request]), + returnValue: _i3.Future<_i2.StreamedResponse>.value( + _FakeStreamedResponse_1( + this, + Invocation.method(#send, [request]), + ), + ), + ) + as _i3.Future<_i2.StreamedResponse>); + + @override + void close() => super.noSuchMethod( + Invocation.method(#close, []), + returnValueForMissingStub: null, + ); +} diff --git a/test/dsym/dsym_command_test.dart b/test/dsym/dsym_command_test.dart new file mode 100644 index 0000000..ce3f1eb --- /dev/null +++ b/test/dsym/dsym_command_test.dart @@ -0,0 +1,99 @@ +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:raygun_cli/src/dsym/dsym_api.dart'; +import 'package:raygun_cli/src/dsym/dsym.dart'; +import 'package:raygun_cli/src/dsym/dsym_command.dart'; +import 'package:test/test.dart'; + +import 'dsym_command_test.mocks.dart'; + +@GenerateMocks([DsymApi]) +void main() { + group('Dsym', () { + late MockDsymApi mockApi; + late DsymCommand command; + late Dsym dsym; + + setUp(() { + mockApi = MockDsymApi(); + command = DsymCommand(api: mockApi); + }); + + group('upload', () { + test('upload calls uploadDsym with correct parameters', () async { + when( + mockApi.uploadDsym( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + ), + ).thenAnswer((_) async => true); + + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--external-access-token=test-token', + '--path=test.zip', + ]); + + dsym = Dsym(command: results, verbose: false, api: mockApi); + + final success = await dsym.upload(); + + expect(success, true); + verify( + mockApi.uploadDsym( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test.zip', + ), + ).called(1); + }); + + test('upload returns false when path is missing', () async { + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--external-access-token=test-token', + ]); + + dsym = Dsym(command: results, verbose: false, api: mockApi); + + final success = await dsym.upload(); + + expect(success, false); + verifyNever( + mockApi.uploadDsym( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + ), + ); + }); + + test( + 'upload returns false when external-access-token is missing', + () async { + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--path=test.zip', + ]); + + dsym = Dsym(command: results, verbose: false, api: mockApi); + + final success = await dsym.upload(); + + expect(success, false); + verifyNever( + mockApi.uploadDsym( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + ), + ); + }, + ); + }); + }); +} diff --git a/test/dsym/dsym_command_test.mocks.dart b/test/dsym/dsym_command_test.mocks.dart new file mode 100644 index 0000000..1821658 --- /dev/null +++ b/test/dsym/dsym_command_test.mocks.dart @@ -0,0 +1,63 @@ +// Mocks generated by Mockito 5.4.6 from annotations +// in raygun_cli/test/dsym/dsym_command_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; + +import 'package:http/http.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:raygun_cli/src/dsym/dsym_api.dart' as _i3; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member + +class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { + _FakeClient_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [DsymApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDsymApi extends _i1.Mock implements _i3.DsymApi { + MockDsymApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.Client get httpClient => + (super.noSuchMethod( + Invocation.getter(#httpClient), + returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), + ) + as _i2.Client); + + @override + _i4.Future uploadDsym({ + required String? appId, + required String? externalAccessToken, + required String? path, + }) => + (super.noSuchMethod( + Invocation.method(#uploadDsym, [], { + #appId: appId, + #externalAccessToken: externalAccessToken, + #path: path, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); +} diff --git a/test/proguard/proguard_api_test.dart b/test/proguard/proguard_api_test.dart index 43c8bc1..6984463 100644 --- a/test/proguard/proguard_api_test.dart +++ b/test/proguard/proguard_api_test.dart @@ -48,30 +48,32 @@ void main() { testFile.deleteSync(); }); - test('returns true when upload is successful with overwrite flag', - () async { - final testFile = File('test_mapping.txt'); - testFile.writeAsStringSync('test proguard mapping content'); - - final response = http.StreamedResponse( - Stream.value(utf8.encode('Upload successful')), - 200, - ); - - when(mockClient.send(any)).thenAnswer((_) async => response); - - final result = await proguardApi.uploadProguardMapping( - appId: 'test-app-id', - externalAccessToken: 'test-token', - path: 'test_mapping.txt', - version: '1.0.0', - overwrite: true, - ); - - expect(result, true); - - testFile.deleteSync(); - }); + test( + 'returns true when upload is successful with overwrite flag', + () async { + final testFile = File('test_mapping.txt'); + testFile.writeAsStringSync('test proguard mapping content'); + + final response = http.StreamedResponse( + Stream.value(utf8.encode('Upload successful')), + 200, + ); + + when(mockClient.send(any)).thenAnswer((_) async => response); + + final result = await proguardApi.uploadProguardMapping( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test_mapping.txt', + version: '1.0.0', + overwrite: true, + ); + + expect(result, true); + + testFile.deleteSync(); + }, + ); test('returns false when upload fails', () async { final testFile = File('test_mapping.txt'); diff --git a/test/proguard/proguard_api_test.mocks.dart b/test/proguard/proguard_api_test.mocks.dart index 5093db9..2659b46 100644 --- a/test/proguard/proguard_api_test.mocks.dart +++ b/test/proguard/proguard_api_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/proguard/proguard_api_test.dart. // Do not manually edit this file. @@ -24,16 +24,17 @@ import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { _FakeResponse_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { _FakeStreamedResponse_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [Client]. @@ -47,26 +48,28 @@ class MockClient extends _i1.Mock implements _i2.Client { @override _i3.Future<_i2.Response> head(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#head, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#head, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#head, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> get(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#get, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#get, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#get, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> post( @@ -76,22 +79,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #post, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #post, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> put( @@ -101,22 +105,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #put, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #put, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> patch( @@ -126,22 +131,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #patch, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #patch, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> delete( @@ -151,34 +157,36 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #delete, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future read(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#read, [url], {#headers: headers}), - returnValue: _i3.Future.value( - _i5.dummyValue( - this, Invocation.method(#read, [url], {#headers: headers}), - ), - ), - ) as _i3.Future); + returnValue: _i3.Future.value( + _i5.dummyValue( + this, + Invocation.method(#read, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future); @override _i3.Future<_i6.Uint8List> readBytes( @@ -186,25 +194,27 @@ class MockClient extends _i1.Mock implements _i2.Client { Map? headers, }) => (super.noSuchMethod( - Invocation.method(#readBytes, [url], {#headers: headers}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) as _i3.Future<_i6.Uint8List>); + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) + as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( - Invocation.method(#send, [request]), - returnValue: _i3.Future<_i2.StreamedResponse>.value( - _FakeStreamedResponse_1( - this, Invocation.method(#send, [request]), - ), - ), - ) as _i3.Future<_i2.StreamedResponse>); + returnValue: _i3.Future<_i2.StreamedResponse>.value( + _FakeStreamedResponse_1( + this, + Invocation.method(#send, [request]), + ), + ), + ) + as _i3.Future<_i2.StreamedResponse>); @override void close() => super.noSuchMethod( - Invocation.method(#close, []), - returnValueForMissingStub: null, - ); + Invocation.method(#close, []), + returnValueForMissingStub: null, + ); } diff --git a/test/proguard/proguard_command_test.dart b/test/proguard/proguard_command_test.dart index c0611dd..8174d92 100644 --- a/test/proguard/proguard_command_test.dart +++ b/test/proguard/proguard_command_test.dart @@ -20,104 +20,105 @@ void main() { }); group('upload', () { - test('upload calls uploadProguardMapping with correct parameters', - () async { - when(mockApi.uploadProguardMapping( - appId: anyNamed('appId'), - externalAccessToken: anyNamed('externalAccessToken'), - path: anyNamed('path'), - version: anyNamed('version'), - overwrite: anyNamed('overwrite'), - )).thenAnswer((_) async => true); - - final parser = command.buildParser(); - final results = parser.parse([ - '--app-id=test-app-id', - '--external-access-token=test-token', - '--path=test.txt', - '--version=1.0.0', - '--overwrite' - ]); - - proguard = Proguard( - command: results, - verbose: false, - api: mockApi, - ); - - final success = await proguard.upload(); - - expect(success, true); - verify(mockApi.uploadProguardMapping( - appId: 'test-app-id', - externalAccessToken: 'test-token', - path: 'test.txt', - version: '1.0.0', - overwrite: true, - )).called(1); - }); - test( - 'upload calls uploadProguardMapping without overwrite when flag not set', - () async { - when(mockApi.uploadProguardMapping( - appId: anyNamed('appId'), - externalAccessToken: anyNamed('externalAccessToken'), - path: anyNamed('path'), - version: anyNamed('version'), - overwrite: anyNamed('overwrite'), - )).thenAnswer((_) async => true); - - final parser = command.buildParser(); - final results = parser.parse([ - '--app-id=test-app-id', - '--external-access-token=test-token', - '--path=test.txt', - '--version=1.0.0' - ]); + 'upload calls uploadProguardMapping with correct parameters', + () async { + when( + mockApi.uploadProguardMapping( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + version: anyNamed('version'), + overwrite: anyNamed('overwrite'), + ), + ).thenAnswer((_) async => true); + + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--external-access-token=test-token', + '--path=test.txt', + '--version=1.0.0', + '--overwrite', + ]); + + proguard = Proguard(command: results, verbose: false, api: mockApi); + + final success = await proguard.upload(); + + expect(success, true); + verify( + mockApi.uploadProguardMapping( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test.txt', + version: '1.0.0', + overwrite: true, + ), + ).called(1); + }, + ); - proguard = Proguard( - command: results, - verbose: false, - api: mockApi, - ); - - final success = await proguard.upload(); - - expect(success, true); - verify(mockApi.uploadProguardMapping( - appId: 'test-app-id', - externalAccessToken: 'test-token', - path: 'test.txt', - version: '1.0.0', - overwrite: false, - )).called(1); - }); + test( + 'upload calls uploadProguardMapping without overwrite when flag not set', + () async { + when( + mockApi.uploadProguardMapping( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + version: anyNamed('version'), + overwrite: anyNamed('overwrite'), + ), + ).thenAnswer((_) async => true); + + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--external-access-token=test-token', + '--path=test.txt', + '--version=1.0.0', + ]); + + proguard = Proguard(command: results, verbose: false, api: mockApi); + + final success = await proguard.upload(); + + expect(success, true); + verify( + mockApi.uploadProguardMapping( + appId: 'test-app-id', + externalAccessToken: 'test-token', + path: 'test.txt', + version: '1.0.0', + overwrite: false, + ), + ).called(1); + }, + ); test('upload returns false when path is missing', () async { final parser = command.buildParser(); final results = parser.parse([ '--app-id=test-app-id', '--external-access-token=test-token', - '--version=1.0.0' + '--version=1.0.0', ]); - proguard = Proguard( - command: results, - verbose: false, - api: mockApi, - ); + proguard = Proguard(command: results, verbose: false, api: mockApi); final success = await proguard.upload(); expect(success, false); - verifyNever(mockApi.uploadProguardMapping( - appId: anyNamed('appId'), - externalAccessToken: anyNamed('externalAccessToken'), - path: anyNamed('path'), - version: anyNamed('version'), - overwrite: anyNamed('overwrite'), - )); + verifyNever( + mockApi.uploadProguardMapping( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + version: anyNamed('version'), + overwrite: anyNamed('overwrite'), + ), + ); }); test('upload returns false when version is missing', () async { @@ -125,50 +126,51 @@ void main() { final results = parser.parse([ '--app-id=test-app-id', '--external-access-token=test-token', - '--path=test.txt' + '--path=test.txt', ]); - proguard = Proguard( - command: results, - verbose: false, - api: mockApi, - ); + proguard = Proguard(command: results, verbose: false, api: mockApi); final success = await proguard.upload(); expect(success, false); - verifyNever(mockApi.uploadProguardMapping( - appId: anyNamed('appId'), - externalAccessToken: anyNamed('externalAccessToken'), - path: anyNamed('path'), - version: anyNamed('version'), - overwrite: anyNamed('overwrite'), - )); - }); - - test('upload returns false when external-access-token is missing', - () async { - final parser = command.buildParser(); - final results = parser.parse( - ['--app-id=test-app-id', '--path=test.txt', '--version=1.0.0']); - - proguard = Proguard( - command: results, - verbose: false, - api: mockApi, + verifyNever( + mockApi.uploadProguardMapping( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + version: anyNamed('version'), + overwrite: anyNamed('overwrite'), + ), ); - - final success = await proguard.upload(); - - expect(success, false); - verifyNever(mockApi.uploadProguardMapping( - appId: anyNamed('appId'), - externalAccessToken: anyNamed('externalAccessToken'), - path: anyNamed('path'), - version: anyNamed('version'), - overwrite: anyNamed('overwrite'), - )); }); + + test( + 'upload returns false when external-access-token is missing', + () async { + final parser = command.buildParser(); + final results = parser.parse([ + '--app-id=test-app-id', + '--path=test.txt', + '--version=1.0.0', + ]); + + proguard = Proguard(command: results, verbose: false, api: mockApi); + + final success = await proguard.upload(); + + expect(success, false); + verifyNever( + mockApi.uploadProguardMapping( + appId: anyNamed('appId'), + externalAccessToken: anyNamed('externalAccessToken'), + path: anyNamed('path'), + version: anyNamed('version'), + overwrite: anyNamed('overwrite'), + ), + ); + }, + ); }); }); } diff --git a/test/proguard/proguard_command_test.mocks.dart b/test/proguard/proguard_command_test.mocks.dart index 935d5c3..3fd9e1d 100644 --- a/test/proguard/proguard_command_test.mocks.dart +++ b/test/proguard/proguard_command_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/proguard/proguard_command_test.dart. // Do not manually edit this file. @@ -22,10 +22,11 @@ import 'package:raygun_cli/src/proguard/proguard_api.dart' as _i3; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { _FakeClient_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [ProguardApi]. @@ -37,10 +38,12 @@ class MockProguardApi extends _i1.Mock implements _i3.ProguardApi { } @override - _i2.Client get httpClient => (super.noSuchMethod( - Invocation.getter(#httpClient), - returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), - ) as _i2.Client); + _i2.Client get httpClient => + (super.noSuchMethod( + Invocation.getter(#httpClient), + returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), + ) + as _i2.Client); @override _i4.Future uploadProguardMapping({ @@ -51,13 +54,14 @@ class MockProguardApi extends _i1.Mock implements _i3.ProguardApi { required bool? overwrite, }) => (super.noSuchMethod( - Invocation.method(#uploadProguardMapping, [], { - #appId: appId, - #externalAccessToken: externalAccessToken, - #path: path, - #version: version, - #overwrite: overwrite, - }), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#uploadProguardMapping, [], { + #appId: appId, + #externalAccessToken: externalAccessToken, + #path: path, + #version: version, + #overwrite: overwrite, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); } diff --git a/test/sourcemaps/sourcemap_api_test.dart b/test/sourcemaps/sourcemap_api_test.dart index 8e103d9..6a7a64e 100644 --- a/test/sourcemaps/sourcemap_api_test.dart +++ b/test/sourcemaps/sourcemap_api_test.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; @@ -37,7 +38,7 @@ void main() { group('uploadSourcemap', () { test('returns true when upload is successful (200)', () async { final response = http.StreamedResponse( - Stream.value([]), + Stream.value(utf8.encode('Upload successful')), 200, ); @@ -56,7 +57,7 @@ void main() { test('returns false when upload fails', () async { final response = http.StreamedResponse( - Stream.value([]), + Stream.value(utf8.encode('Bad request')), 400, ); diff --git a/test/sourcemaps/sourcemap_api_test.mocks.dart b/test/sourcemaps/sourcemap_api_test.mocks.dart index 6a8c7ec..d1f2c09 100644 --- a/test/sourcemaps/sourcemap_api_test.mocks.dart +++ b/test/sourcemaps/sourcemap_api_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/sourcemaps/sourcemap_api_test.dart. // Do not manually edit this file. @@ -24,16 +24,17 @@ import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { _FakeResponse_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { _FakeStreamedResponse_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [Client]. @@ -47,26 +48,28 @@ class MockClient extends _i1.Mock implements _i2.Client { @override _i3.Future<_i2.Response> head(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#head, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#head, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#head, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> get(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#get, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#get, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#get, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> post( @@ -76,22 +79,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #post, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #post, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> put( @@ -101,22 +105,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #put, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #put, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> patch( @@ -126,22 +131,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #patch, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #patch, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> delete( @@ -151,34 +157,36 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #delete, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future read(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#read, [url], {#headers: headers}), - returnValue: _i3.Future.value( - _i5.dummyValue( - this, Invocation.method(#read, [url], {#headers: headers}), - ), - ), - ) as _i3.Future); + returnValue: _i3.Future.value( + _i5.dummyValue( + this, + Invocation.method(#read, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future); @override _i3.Future<_i6.Uint8List> readBytes( @@ -186,25 +194,27 @@ class MockClient extends _i1.Mock implements _i2.Client { Map? headers, }) => (super.noSuchMethod( - Invocation.method(#readBytes, [url], {#headers: headers}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) as _i3.Future<_i6.Uint8List>); + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) + as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( - Invocation.method(#send, [request]), - returnValue: _i3.Future<_i2.StreamedResponse>.value( - _FakeStreamedResponse_1( - this, Invocation.method(#send, [request]), - ), - ), - ) as _i3.Future<_i2.StreamedResponse>); + returnValue: _i3.Future<_i2.StreamedResponse>.value( + _FakeStreamedResponse_1( + this, + Invocation.method(#send, [request]), + ), + ), + ) + as _i3.Future<_i2.StreamedResponse>); @override void close() => super.noSuchMethod( - Invocation.method(#close, []), - returnValueForMissingStub: null, - ); + Invocation.method(#close, []), + returnValueForMissingStub: null, + ); } diff --git a/test/sourcemaps/sourcemap_command_test.dart b/test/sourcemaps/sourcemap_command_test.dart index 2ccf4f8..98a1f0f 100644 --- a/test/sourcemaps/sourcemap_command_test.dart +++ b/test/sourcemaps/sourcemap_command_test.dart @@ -35,12 +35,14 @@ void main() { group('run', () { test('handles single file upload (platform null)', () async { - when(mockApi.uploadSourcemap( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - uri: anyNamed('uri'), - )).thenAnswer((_) async => true); + when( + mockApi.uploadSourcemap( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + uri: anyNamed('uri'), + ), + ).thenAnswer((_) async => true); final parser = command.buildParser(); @@ -53,27 +55,28 @@ void main() { '--input-map=${testFile.path}', ]); - final result = await command.run( - command: results, - verbose: false, - ); + final result = await command.run(command: results, verbose: false); expect(result, true); - verify(mockApi.uploadSourcemap( - appId: 'test-app-id', - token: 'test-token', - path: testFile.path, - uri: 'http://test.com/app.js', - )).called(1); + verify( + mockApi.uploadSourcemap( + appId: 'test-app-id', + token: 'test-token', + path: testFile.path, + uri: 'http://test.com/app.js', + ), + ).called(1); }); test('handles flutter platform upload', () async { - when(mockApi.uploadSourcemap( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - uri: anyNamed('uri'), - )).thenAnswer((_) async => true); + when( + mockApi.uploadSourcemap( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + uri: anyNamed('uri'), + ), + ).thenAnswer((_) async => true); final parser = command.buildParser(); @@ -83,21 +86,20 @@ void main() { '--platform=flutter', '--app-id=test-app-id', '--token=test-token', - '--base-uri=http://test.com/' + '--base-uri=http://test.com/', ]); - final result = await command.run( - command: results, - verbose: false, - ); + final result = await command.run(command: results, verbose: false); expect(result, true); - verify(mockApi.uploadSourcemap( - appId: 'test-app-id', - token: 'test-token', - path: 'build/web/main.dart.js.map', - uri: 'http://test.com/main.dart.js', - )).called(1); + verify( + mockApi.uploadSourcemap( + appId: 'test-app-id', + token: 'test-token', + path: 'build/web/main.dart.js.map', + uri: 'http://test.com/main.dart.js', + ), + ).called(1); }); test('handles node platform (returns false - not implemented)', () async { @@ -106,22 +108,21 @@ void main() { '--platform=node', '--app-id=test-app-id', '--token=test-token', - '--base-uri=http://test.com/' + '--base-uri=http://test.com/', ]); - final result = await command.run( - command: results, - verbose: false, - ); + final result = await command.run(command: results, verbose: false); // The "node" platform is not implemented, so it should return false expect(result, false); - verifyNever(mockApi.uploadSourcemap( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - uri: anyNamed('uri'), - )); + verifyNever( + mockApi.uploadSourcemap( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + uri: anyNamed('uri'), + ), + ); }); test('returns false for unsupported platform', () async { @@ -129,13 +130,10 @@ void main() { final results = parser.parse([ '--platform=unsupported', '--app-id=test-app-id', - '--token=test-token' + '--token=test-token', ]); - final result = await command.run( - command: results, - verbose: false, - ); + final result = await command.run(command: results, verbose: false); expect(result, false); }); @@ -144,10 +142,7 @@ void main() { final parser = command.buildParser(); final results = parser.parse(['--help']); - final result = await command.run( - command: results, - verbose: false, - ); + final result = await command.run(command: results, verbose: false); expect(result, false); }); diff --git a/test/sourcemaps/sourcemap_command_test.mocks.dart b/test/sourcemaps/sourcemap_command_test.mocks.dart index ebe6399..28b7ec0 100644 --- a/test/sourcemaps/sourcemap_command_test.mocks.dart +++ b/test/sourcemaps/sourcemap_command_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/sourcemaps/sourcemap_command_test.dart. // Do not manually edit this file. @@ -22,10 +22,11 @@ import 'package:raygun_cli/src/sourcemap/sourcemap_api.dart' as _i3; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { _FakeClient_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [SourcemapApi]. @@ -37,10 +38,12 @@ class MockSourcemapApi extends _i1.Mock implements _i3.SourcemapApi { } @override - _i2.Client get httpClient => (super.noSuchMethod( - Invocation.getter(#httpClient), - returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), - ) as _i2.Client); + _i2.Client get httpClient => + (super.noSuchMethod( + Invocation.getter(#httpClient), + returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), + ) + as _i2.Client); @override _i4.Future uploadSourcemap({ @@ -50,12 +53,13 @@ class MockSourcemapApi extends _i1.Mock implements _i3.SourcemapApi { required String? uri, }) => (super.noSuchMethod( - Invocation.method(#uploadSourcemap, [], { - #appId: appId, - #token: token, - #path: path, - #uri: uri, - }), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#uploadSourcemap, [], { + #appId: appId, + #token: token, + #path: path, + #uri: uri, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); } diff --git a/test/symbols/symbols_api_test.dart b/test/symbols/symbols_api_test.dart index 8e0c091..28609c3 100644 --- a/test/symbols/symbols_api_test.dart +++ b/test/symbols/symbols_api_test.dart @@ -26,10 +26,7 @@ void main() { final testFile = File('test_symbols.txt'); testFile.writeAsStringSync('test symbols content'); - final response = http.StreamedResponse( - Stream.value([]), - 200, - ); + final response = http.StreamedResponse(Stream.value([]), 200); when(mockClient.send(any)).thenAnswer((_) async => response); @@ -51,10 +48,7 @@ void main() { final testFile = File('test_symbols.txt'); testFile.writeAsStringSync('test symbols content'); - final response = http.StreamedResponse( - Stream.value([]), - 201, - ); + final response = http.StreamedResponse(Stream.value([]), 201); when(mockClient.send(any)).thenAnswer((_) async => response); @@ -109,16 +103,8 @@ void main() { group('listSymbols', () { test('returns true and prints symbols when successful', () async { final symbolsResponse = [ - { - 'name': 'symbols1.txt', - 'identifier': 'id1', - 'version': '1.0.0', - }, - { - 'name': 'symbols2.txt', - 'identifier': 'id2', - 'version': '1.1.0', - }, + {'name': 'symbols1.txt', 'identifier': 'id1', 'version': '1.0.0'}, + {'name': 'symbols2.txt', 'identifier': 'id2', 'version': '1.1.0'}, ]; final response = http.StreamedResponse( @@ -138,10 +124,7 @@ void main() { }); test('returns false when list request fails', () async { - final response = http.StreamedResponse( - Stream.value([]), - 401, - ); + final response = http.StreamedResponse(Stream.value([]), 401); when(mockClient.send(any)).thenAnswer((_) async => response); @@ -156,10 +139,7 @@ void main() { group('deleteSymbols', () { test('returns true when delete is successful', () async { - final response = http.StreamedResponse( - Stream.value([]), - 204, - ); + final response = http.StreamedResponse(Stream.value([]), 204); when(mockClient.send(any)).thenAnswer((_) async => response); @@ -174,10 +154,7 @@ void main() { }); test('returns false when delete fails', () async { - final response = http.StreamedResponse( - Stream.value([]), - 404, - ); + final response = http.StreamedResponse(Stream.value([]), 404); when(mockClient.send(any)).thenAnswer((_) async => response); diff --git a/test/symbols/symbols_api_test.mocks.dart b/test/symbols/symbols_api_test.mocks.dart index 7557509..9ed7425 100644 --- a/test/symbols/symbols_api_test.mocks.dart +++ b/test/symbols/symbols_api_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/symbols/symbols_api_test.dart. // Do not manually edit this file. @@ -24,16 +24,17 @@ import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { _FakeResponse_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { _FakeStreamedResponse_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [Client]. @@ -47,26 +48,28 @@ class MockClient extends _i1.Mock implements _i2.Client { @override _i3.Future<_i2.Response> head(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#head, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#head, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#head, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> get(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#get, [url], {#headers: headers}), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method(#get, [url], {#headers: headers}), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method(#get, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> post( @@ -76,22 +79,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #post, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #post, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #post, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> put( @@ -101,22 +105,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #put, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #put, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #put, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> patch( @@ -126,22 +131,23 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #patch, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #patch, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future<_i2.Response> delete( @@ -151,34 +157,36 @@ class MockClient extends _i1.Mock implements _i2.Client { _i4.Encoding? encoding, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [url], - {#headers: headers, #body: body, #encoding: encoding}, - ), - returnValue: _i3.Future<_i2.Response>.value( - _FakeResponse_0( - this, Invocation.method( #delete, [url], {#headers: headers, #body: body, #encoding: encoding}, ), - ), - ), - ) as _i3.Future<_i2.Response>); + returnValue: _i3.Future<_i2.Response>.value( + _FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + {#headers: headers, #body: body, #encoding: encoding}, + ), + ), + ), + ) + as _i3.Future<_i2.Response>); @override _i3.Future read(Uri? url, {Map? headers}) => (super.noSuchMethod( - Invocation.method(#read, [url], {#headers: headers}), - returnValue: _i3.Future.value( - _i5.dummyValue( - this, Invocation.method(#read, [url], {#headers: headers}), - ), - ), - ) as _i3.Future); + returnValue: _i3.Future.value( + _i5.dummyValue( + this, + Invocation.method(#read, [url], {#headers: headers}), + ), + ), + ) + as _i3.Future); @override _i3.Future<_i6.Uint8List> readBytes( @@ -186,25 +194,27 @@ class MockClient extends _i1.Mock implements _i2.Client { Map? headers, }) => (super.noSuchMethod( - Invocation.method(#readBytes, [url], {#headers: headers}), - returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) as _i3.Future<_i6.Uint8List>); + Invocation.method(#readBytes, [url], {#headers: headers}), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) + as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( - Invocation.method(#send, [request]), - returnValue: _i3.Future<_i2.StreamedResponse>.value( - _FakeStreamedResponse_1( - this, Invocation.method(#send, [request]), - ), - ), - ) as _i3.Future<_i2.StreamedResponse>); + returnValue: _i3.Future<_i2.StreamedResponse>.value( + _FakeStreamedResponse_1( + this, + Invocation.method(#send, [request]), + ), + ), + ) + as _i3.Future<_i2.StreamedResponse>); @override void close() => super.noSuchMethod( - Invocation.method(#close, []), - returnValueForMissingStub: null, - ); + Invocation.method(#close, []), + returnValueForMissingStub: null, + ); } diff --git a/test/symbols/symbols_command_test.dart b/test/symbols/symbols_command_test.dart index 56280a5..58e05f3 100644 --- a/test/symbols/symbols_command_test.dart +++ b/test/symbols/symbols_command_test.dart @@ -19,37 +19,46 @@ void main() { }); group('run', () { - test('upload command calls uploadSymbols with correct parameters', - () async { - when(mockApi.uploadSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - version: anyNamed('version'), - )).thenAnswer((_) async => true); - - final parser = ArgParser(); - parser.addCommand('upload'); - parser.addOption('path'); - parser.addOption('version'); - - final results = - parser.parse(['upload', '--path=test.txt', '--version=1.0.0']); - - final success = await command.run( - command: results, - appId: 'test-app-id', - token: 'test-token', - ); - - expect(success, true); - verify(mockApi.uploadSymbols( - appId: 'test-app-id', - token: 'test-token', - path: 'test.txt', - version: '1.0.0', - )).called(1); - }); + test( + 'upload command calls uploadSymbols with correct parameters', + () async { + when( + mockApi.uploadSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + version: anyNamed('version'), + ), + ).thenAnswer((_) async => true); + + final parser = ArgParser(); + parser.addCommand('upload'); + parser.addOption('path'); + parser.addOption('version'); + + final results = parser.parse([ + 'upload', + '--path=test.txt', + '--version=1.0.0', + ]); + + final success = await command.run( + command: results, + appId: 'test-app-id', + token: 'test-token', + ); + + expect(success, true); + verify( + mockApi.uploadSymbols( + appId: 'test-app-id', + token: 'test-token', + path: 'test.txt', + version: '1.0.0', + ), + ).called(1); + }, + ); test('upload command returns false when path is missing', () async { final parser = ArgParser(); @@ -66,12 +75,14 @@ void main() { ); expect(success, false); - verifyNever(mockApi.uploadSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - version: anyNamed('version'), - )); + verifyNever( + mockApi.uploadSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + version: anyNamed('version'), + ), + ); }); test('upload command returns false when version is missing', () async { @@ -89,19 +100,23 @@ void main() { ); expect(success, false); - verifyNever(mockApi.uploadSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - path: anyNamed('path'), - version: anyNamed('version'), - )); + verifyNever( + mockApi.uploadSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + path: anyNamed('path'), + version: anyNamed('version'), + ), + ); }); test('list command calls listSymbols with correct parameters', () async { - when(mockApi.listSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - )).thenAnswer((_) async => true); + when( + mockApi.listSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + ), + ).thenAnswer((_) async => true); final parser = ArgParser(); parser.addCommand('list'); @@ -115,39 +130,44 @@ void main() { ); expect(success, true); - verify(mockApi.listSymbols( - appId: 'test-app-id', - token: 'test-token', - )).called(1); + verify( + mockApi.listSymbols(appId: 'test-app-id', token: 'test-token'), + ).called(1); }); - test('delete command calls deleteSymbols with correct parameters', - () async { - when(mockApi.deleteSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - id: anyNamed('id'), - )).thenAnswer((_) async => true); - - final parser = ArgParser(); - parser.addCommand('delete'); - parser.addOption('id'); - - final results = parser.parse(['delete', '--id=symbol-id']); - - final success = await command.run( - command: results, - appId: 'test-app-id', - token: 'test-token', - ); - - expect(success, true); - verify(mockApi.deleteSymbols( - appId: 'test-app-id', - token: 'test-token', - id: 'symbol-id', - )).called(1); - }); + test( + 'delete command calls deleteSymbols with correct parameters', + () async { + when( + mockApi.deleteSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + id: anyNamed('id'), + ), + ).thenAnswer((_) async => true); + + final parser = ArgParser(); + parser.addCommand('delete'); + parser.addOption('id'); + + final results = parser.parse(['delete', '--id=symbol-id']); + + final success = await command.run( + command: results, + appId: 'test-app-id', + token: 'test-token', + ); + + expect(success, true); + verify( + mockApi.deleteSymbols( + appId: 'test-app-id', + token: 'test-token', + id: 'symbol-id', + ), + ).called(1); + }, + ); test('delete command returns false when id is missing', () async { final parser = ArgParser(); @@ -163,11 +183,13 @@ void main() { ); expect(success, false); - verifyNever(mockApi.deleteSymbols( - appId: anyNamed('appId'), - token: anyNamed('token'), - id: anyNamed('id'), - )); + verifyNever( + mockApi.deleteSymbols( + appId: anyNamed('appId'), + token: anyNamed('token'), + id: anyNamed('id'), + ), + ); }); test('returns false for unknown command', () async { diff --git a/test/symbols/symbols_command_test.mocks.dart b/test/symbols/symbols_command_test.mocks.dart index cdbd519..7d66cca 100644 --- a/test/symbols/symbols_command_test.mocks.dart +++ b/test/symbols/symbols_command_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.5 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in raygun_cli/test/symbols/symbols_command_test.dart. // Do not manually edit this file. @@ -22,10 +22,11 @@ import 'package:raygun_cli/src/symbols/symbols_api.dart' as _i3; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { _FakeClient_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + : super(parent, parentInvocation); } /// A class which mocks [SymbolsApi]. @@ -37,10 +38,12 @@ class MockSymbolsApi extends _i1.Mock implements _i3.SymbolsApi { } @override - _i2.Client get httpClient => (super.noSuchMethod( - Invocation.getter(#httpClient), - returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), - ) as _i2.Client); + _i2.Client get httpClient => + (super.noSuchMethod( + Invocation.getter(#httpClient), + returnValue: _FakeClient_0(this, Invocation.getter(#httpClient)), + ) + as _i2.Client); @override _i4.Future uploadSymbols({ @@ -50,14 +53,15 @@ class MockSymbolsApi extends _i1.Mock implements _i3.SymbolsApi { required String? version, }) => (super.noSuchMethod( - Invocation.method(#uploadSymbols, [], { - #appId: appId, - #token: token, - #path: path, - #version: version, - }), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#uploadSymbols, [], { + #appId: appId, + #token: token, + #path: path, + #version: version, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); @override _i4.Future listSymbols({ @@ -65,9 +69,10 @@ class MockSymbolsApi extends _i1.Mock implements _i3.SymbolsApi { required String? token, }) => (super.noSuchMethod( - Invocation.method(#listSymbols, [], {#appId: appId, #token: token}), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#listSymbols, [], {#appId: appId, #token: token}), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); @override _i4.Future deleteSymbols({ @@ -76,11 +81,12 @@ class MockSymbolsApi extends _i1.Mock implements _i3.SymbolsApi { required String? id, }) => (super.noSuchMethod( - Invocation.method(#deleteSymbols, [], { - #appId: appId, - #token: token, - #id: id, - }), - returnValue: _i4.Future.value(false), - ) as _i4.Future); + Invocation.method(#deleteSymbols, [], { + #appId: appId, + #token: token, + #id: id, + }), + returnValue: _i4.Future.value(false), + ) + as _i4.Future); }