Skip to content

Commit

Permalink
chore(api): added unit test for GraphQL custom types (#4892)
Browse files Browse the repository at this point in the history
  • Loading branch information
Equartey authored and Jordan-Nelson committed Jun 27, 2024
1 parent b743260 commit 184dcbc
Show file tree
Hide file tree
Showing 6 changed files with 454 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@

// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, override_on_non_overriding_member, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously

import 'ModelProvider.dart';
import 'package:amplify_core/amplify_core.dart' as amplify_core;
import 'package:collection/collection.dart';

import 'ModelProvider.dart';

/** This is an auto generated class representing the CustomTypeWithAppsyncScalarTypes type in your schema. */
class CustomTypeWithAppsyncScalarTypes {
final String? _stringValue;
Expand Down Expand Up @@ -738,14 +739,17 @@ class CustomTypeWithAppsyncScalarTypes {
.toList()
: null,
_customTypeValue = json['customTypeValue'] != null
? SimpleCustomType.fromJson(
new Map<String, dynamic>.from(json['customTypeValue']))
? json['customTypeValue']['serializedData'] != null
? SimpleCustomType.fromJson(new Map<String, dynamic>.from(
json['customTypeValue']['serializedData']))
: SimpleCustomType.fromJson(
new Map<String, dynamic>.from(json['customTypeValue']))
: null,
_listOfCustomTypeValue = json['listOfCustomTypeValue'] is List
? (json['listOfCustomTypeValue'] as List)
.where((e) => e != null)
.map((e) =>
SimpleCustomType.fromJson(new Map<String, dynamic>.from(e)))
.map((e) => SimpleCustomType.fromJson(
new Map<String, dynamic>.from(e['serializedData'] ?? e)))
.toList()
: null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@

// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, override_on_non_overriding_member, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously

import 'ModelProvider.dart';
import 'package:amplify_core/amplify_core.dart' as amplify_core;
import 'package:collection/collection.dart';

import 'ModelProvider.dart';

/** This is an auto generated class representing the ModelWithCustomType type in your schema. */
class ModelWithCustomType extends amplify_core.Model {
static const classType = const _ModelWithCustomTypeModelType();
Expand Down Expand Up @@ -152,14 +153,18 @@ class ModelWithCustomType extends amplify_core.Model {
ModelWithCustomType.fromJson(Map<String, dynamic> json)
: id = json['id'],
_customTypeValue = json['customTypeValue'] != null
? CustomTypeWithAppsyncScalarTypes.fromJson(
new Map<String, dynamic>.from(json['customTypeValue']))
? json['customTypeValue']['serializedData'] != null
? CustomTypeWithAppsyncScalarTypes.fromJson(
new Map<String, dynamic>.from(
json['customTypeValue']['serializedData']))
: CustomTypeWithAppsyncScalarTypes.fromJson(
new Map<String, dynamic>.from(json['customTypeValue']))
: null,
_listOfCustomTypeValue = json['listOfCustomTypeValue'] is List
? (json['listOfCustomTypeValue'] as List)
.where((e) => e != null)
.map((e) => CustomTypeWithAppsyncScalarTypes.fromJson(
new Map<String, dynamic>.from(e)))
new Map<String, dynamic>.from(e['serializedData'] ?? e)))
.toList()
: null,
_createdAt = json['createdAt'] != null
Expand Down
164 changes: 75 additions & 89 deletions packages/api/amplify_api_dart/test/graphql_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,92 +14,21 @@ import 'package:aws_common/testing.dart';
import 'package:collection/collection.dart';
import 'package:test/test.dart';

import 'mocks.dart';
import 'test_data/fake_amplify_configuration.dart';
import 'test_models/ModelProvider.dart';
import 'util.dart';

final _deepEquals = const DeepCollectionEquality().equals;

// Success Mocks
const _expectedQuerySuccessResponseBody = {
'data': {
'listBlogs': {
'items': [
{
'id': 'TEST_ID',
'name': 'Test App Blog',
'createdAt': '2022-06-28T17:36:52.460Z',
}
],
},
},
};

final _modelQueryId = uuid();
final _expectedModelQueryResult = {
'data': {
'getBlog': {
'createdAt': '2021-07-21T22:23:33.707Z',
'id': _modelQueryId,
'name': 'Test App Blog',
},
},
};
const _expectedMutateSuccessResponseBody = {
'data': {
'createBlog': {
'id': 'TEST_ID',
'name': 'Test App Blog',
'createdAt': '2022-07-06T18:42:26.126Z',
},
},
};

// Error Mocks
const _errorMessage = 'Unable to parse GraphQL query.';
const _errorLocations = [
{'line': 2, 'column': 3},
{'line': 4, 'column': 5},
];
const _errorPath = ['a', 1, 'b'];
const _errorExtensions = {
'a': 'blah',
'b': {'c': 'd'},
};
const _errorType = 'DynamoDB:ConditionalCheckFailedException';
const _errorInfo = {'a': 'b'};
const _expectedErrorResponseBody = {
'data': null,
'errors': [
{
'message': _errorMessage,
'locations': _errorLocations,
'path': _errorPath,
'extensions': _errorExtensions,
'errorType': _errorType,
'errorInfo': _errorInfo,
},
],
};

const _authErrorMessage = 'Not authorized';
const _expectedAuthErrorResponseBody = {
'data': null,
'errors': [
{
'message': _authErrorMessage,
},
],
};

final mockHttpClient = MockAWSHttpClient((request, _) async {
if (request.headers[xApiKey] != 'abc123' &&
request.headers[AWSHeaders.authorization] == testFunctionToken) {
// Not authorized w API key but has lambda auth token, mock that auth mode
// does not work for this query.
return AWSHttpResponse(
statusCode: 401,
body: utf8.encode(json.encode(_expectedAuthErrorResponseBody)),
body: utf8.encode(json.encode(expectedAuthErrorResponseBody)),
);
}
if (request.headers[xApiKey] != 'abc123') {
Expand All @@ -110,25 +39,32 @@ final mockHttpClient = MockAWSHttpClient((request, _) async {
if (body.contains('getBlog')) {
return AWSHttpResponse(
statusCode: 200,
body: utf8.encode(json.encode(_expectedModelQueryResult)),
body: utf8.encode(json.encode(expectedModelQueryResult)),
);
}
if (body.contains('TestMutate')) {
return AWSHttpResponse(
statusCode: 400,
body: utf8.encode(json.encode(_expectedMutateSuccessResponseBody)),
body: utf8.encode(json.encode(expectedMutateSuccessResponseBody)),
);
}
if (body.contains('TestError')) {
return AWSHttpResponse(
statusCode: 400,
body: utf8.encode(json.encode(_expectedErrorResponseBody)),
body: utf8.encode(json.encode(expectedErrorResponseBody)),
);
}
if (body.contains('createModelWithCustomType')) {
return AWSHttpResponse(
statusCode: 200,
body: utf8
.encode(json.encode(expectedModelWithCustomTypeSuccessResponseBody)),
);
}

return AWSHttpResponse(
statusCode: 400,
body: utf8.encode((json.encode(_expectedQuerySuccessResponseBody))),
body: utf8.encode((json.encode(expectedQuerySuccessResponseBody))),
);
});

Expand Down Expand Up @@ -195,7 +131,7 @@ void main() {
final operation = Amplify.API.query(request: req);
final res = await operation.response;

final expected = json.encode(_expectedQuerySuccessResponseBody['data']);
final expected = json.encode(expectedQuerySuccessResponseBody['data']);

expect(res.data, equals(expected));
expect(res.errors, isEmpty);
Expand All @@ -219,7 +155,7 @@ void main() {
final operation = Amplify.API.query(request: req);
final res = await operation.response;

final expected = json.encode(_expectedQuerySuccessResponseBody['data']);
final expected = json.encode(expectedQuerySuccessResponseBody['data']);

expect(res.data, equals(expected));
expect(res.errors, isEmpty);
Expand All @@ -242,7 +178,23 @@ void main() {
final operation = Amplify.API.mutate(request: req);
final res = await operation.response;

final expected = json.encode(_expectedMutateSuccessResponseBody['data']);
final expected = json.encode(expectedMutateSuccessResponseBody['data']);

expect(res.data, equals(expected));
expect(res.errors, isEmpty);
});

test('Mutate returns proper response.data for custom types', () async {
final req = GraphQLRequest<String>(
document: modelWithCustomTypeDocument,
variables: modelWithCustomTypeVariables,
);

final operation = Amplify.API.mutate(request: req);
final res = await operation.response;

final expected =
json.encode(expectedModelWithCustomTypeSuccessResponseBody['data']);

expect(res.data, equals(expected));
expect(res.errors, isEmpty);
Expand All @@ -255,23 +207,57 @@ void main() {
const expectedDoc =
'query getBlog(\$id: ID!) { getBlog(id: \$id) { $blogSelectionSet } }';
const decodePath = 'getBlog';
final blog = Blog(id: _modelQueryId, name: 'Lorem ipsum $_modelQueryId');
final blog = Blog(id: modelQueryId, name: 'Lorem ipsum $modelQueryId');
final req = ModelQueries.get<Blog>(Blog.classType, blog.modelIdentifier);

final operation = Amplify.API.query(request: req);
final res = await operation.response;

// request asserts
expect(req.document, expectedDoc);
expect(_deepEquals(req.variables, {'id': _modelQueryId}), isTrue);
expect(_deepEquals(req.variables, {'id': modelQueryId}), isTrue);
expect(req.modelType, Blog.classType);
expect(req.decodePath, decodePath);
// response asserts
expect(res.data, isA<Blog>());
expect(res.data?.id, _modelQueryId);
expect(res.data?.id, modelQueryId);
expect(res.errors, isEmpty);
});

test(
'Mutation.create returns proper response.data for Models with custom types',
() async {
const expectedDoc = modelWithCustomTypeDocument;
const decodePath = 'createModelWithCustomType';
final req = ModelMutations.create<ModelWithCustomType>(
modelWithCustomType,
);

final operation = Amplify.API.query(request: req);
final res = await operation.response;

// request asserts
expect(req.document, expectedDoc);
expect(_deepEquals(req.variables, modelWithCustomTypeVariables), isTrue);
expect(req.modelType, ModelWithCustomType.classType);
expect(req.decodePath, decodePath);
// response asserts
expect(res.data, isA<ModelWithCustomType>());
expect(res.data?.id, modelCustomTypeId);

final data = res.data!;
expect(
data.customTypeValue,
equals(modelWithCustomType.customTypeValue),
);
expect(
data.listOfCustomTypeValue,
equals(modelWithCustomType.listOfCustomTypeValue),
);
expect(res.errors, isEmpty);
});
});

const graphQLDocument = '''subscription MySubscription {
onCreateBlog {
id
Expand Down Expand Up @@ -550,15 +536,15 @@ void main() {
final res = await operation.response;

const errorExpected = GraphQLResponseError(
message: _errorMessage,
message: errorMessage,
locations: [
GraphQLResponseErrorLocation(2, 3),
GraphQLResponseErrorLocation(4, 5),
],
path: <dynamic>[..._errorPath],
extensions: <String, dynamic>{..._errorExtensions},
errorType: _errorType,
errorInfo: _errorInfo,
path: <dynamic>[...errorPath],
extensions: <String, dynamic>{...errorExtensions},
errorType: errorType,
errorInfo: errorInfo,
);

expect(res.data, equals(null));
Expand All @@ -585,7 +571,7 @@ void main() {
final res = await operation.response;

const errorExpected = GraphQLResponseError(
message: _authErrorMessage,
message: authErrorMessage,
);

expect(res.data, equals(null));
Expand Down
Loading

0 comments on commit 184dcbc

Please sign in to comment.