Skip to content

Commit

Permalink
chore: clean up & refactor test
Browse files Browse the repository at this point in the history
  • Loading branch information
Equartey committed Apr 12, 2024
1 parent 71de357 commit 996fbe6
Show file tree
Hide file tree
Showing 7 changed files with 533 additions and 74 deletions.
10 changes: 5 additions & 5 deletions packages/amplify_core/lib/src/types/query/query_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,19 @@ class QueryField<T> {
/// Matches models whether the given field exists or not.
///
/// ### Example:
/// The example returns Post where the Blog attribute is not exists.
/// The example returns Blog where the optional Author attribute exists.
///
/// ```dart
/// ModelQueries.list(
/// Post.classType,
/// where: Post.BLOG.attributeExists(value: true),
/// Blog.classType,
/// where: Blog.AUTHOR.attributeExists(),
/// );
/// ```
QueryPredicateOperation attributeExists({bool? value}) =>
QueryPredicateOperation attributeExists({bool exists = true}) =>
QueryPredicateOperation(
fieldName,
AttributeExistsQueryOperator(
value ?? false,
exists: exists,
),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ class BeginsWithQueryOperator extends QueryFieldOperatorSingleValue<String> {
}

class AttributeExistsQueryOperator extends QueryFieldOperatorSingleValue<bool> {
const AttributeExistsQueryOperator(bool value)
: super(value, QueryFieldOperatorType.attribute_exists);
const AttributeExistsQueryOperator({bool exists = true})
: super(exists, QueryFieldOperatorType.attribute_exists);

@override
bool evaluate(bool? other) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
type Blog @model @auth(rules: [
{ allow: public, operations: [read], provider: apiKey},
{ allow: public, operations: [read], provider: iam},
{ allow: private, operations: [read], provider: iam},
{ allow: private, operations: [read], provider: userPools},
{ allow: owner, operations: [create, read, update, delete] }
]) {
type Blog
@model
@auth(
rules: [
{ allow: public, operations: [read], provider: apiKey }
{ allow: public, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: userPools }
{ allow: owner, operations: [create, read, update, delete] }
]
) {
id: ID!
name: String!
posts: [Post] @hasMany(indexName: "byBlog", fields: ["id"])
}

type Post @model @auth(rules: [
{ allow: public, operations: [read], provider: iam},
{ allow: private, operations: [read], provider: iam},
{ allow: private, operations: [read], provider: userPools},
{ allow: owner, operations: [create, read, update, delete] }
]) {
type Post
@model
@auth(
rules: [
{ allow: public, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: userPools }
{ allow: owner, operations: [create, read, update, delete] }
]
) {
id: ID!
title: String!
rating: Int!
Expand All @@ -24,37 +32,41 @@ type Post @model @auth(rules: [
comments: [Comment] @hasMany(indexName: "byPost", fields: ["id"])
}

type Comment @model @auth(rules: [
{ allow: private, operations: [read], provider: iam},
{ allow: private, operations: [read], provider: userPools},
{ allow: owner, operations: [create, read, update, delete] }
]) {
type Comment
@model
@auth(
rules: [
{ allow: private, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: userPools }
{ allow: owner, operations: [create, read, update, delete] }
]
) {
id: ID!
postID: ID! @index(name: "byPost")
post: Post @belongsTo(fields: ["postID"])
content: String!
}

type CpkOneToOneBidirectionalParentCD @model @auth(rules: [
{ allow: private, provider: iam}
]) {
type CpkOneToOneBidirectionalParentCD
@model
@auth(rules: [{ allow: private, provider: iam }]) {
customId: ID! @primaryKey(sortKeyFields: ["name"])
name: String!
implicitChild: CpkOneToOneBidirectionalChildImplicitCD @hasOne
explicitChild: CpkOneToOneBidirectionalChildExplicitCD @hasOne
}

type CpkOneToOneBidirectionalChildImplicitCD @model @auth(rules: [
{ allow: private, provider: iam}
]) {
type CpkOneToOneBidirectionalChildImplicitCD
@model
@auth(rules: [{ allow: private, provider: iam }]) {
id: ID! @primaryKey(sortKeyFields: ["name"])
name: String!
belongsToParent: CpkOneToOneBidirectionalParentCD @belongsTo
}

type CpkOneToOneBidirectionalChildExplicitCD @model @auth(rules: [
{ allow: private, provider: iam}
]) {
type CpkOneToOneBidirectionalChildExplicitCD
@model
@auth(rules: [{ allow: private, provider: iam }]) {
id: ID! @primaryKey(sortKeyFields: ["name"])
name: String!
belongsToParentID: ID
Expand All @@ -63,22 +75,41 @@ type CpkOneToOneBidirectionalChildExplicitCD @model @auth(rules: [
@belongsTo(fields: ["belongsToParentID", "belongsToParentName"])
}

type OwnerOnly @model @auth(rules: [{allow: owner}]) {
type OwnerOnly @model @auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
}
}

type lowerCase
@model
@auth(
rules: [
{ allow: public, operations: [read], provider: apiKey },
{ allow: public, operations: [read], provider: iam },
{ allow: private, operations: [read], provider: iam },
{ allow: private, operations: [read], provider: userPools },
{ allow: public, operations: [read], provider: apiKey }
{ allow: public, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: userPools }
{ allow: owner, operations: [create, read, update, delete] }
]
) {
id: ID!
name: String!
}
}

type Sample
@model
@auth(
rules: [
{ allow: public, operations: [read], provider: apiKey }
{ allow: public, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: iam }
{ allow: private, operations: [read], provider: userPools }
{ allow: owner, operations: [create, read, update, delete] }
]
) {
id: ID!
name: String
number: Int
flag: Boolean
date: AWSTime
rootbeer: Float
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_api_example/models/ModelProvider.dart';
Expand All @@ -20,6 +21,8 @@ import '../util.dart';
/// increase past the default limit.
const _limit = 10000;

const _max = 10000;

void main({bool useExistingTestUser = false}) {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

Expand Down Expand Up @@ -62,38 +65,6 @@ void main({bool useExistingTestUser = false}) {
expect(data[listBlogs][items], hasLength(greaterThanOrEqualTo(0)));
});

testWidgets('should attributeExists request',
(WidgetTester tester) async {
final title = 'Lorem Ipsum Test Post: ${uuid()}';
const rating = 0;
final createdPost = await addPostAndBlog(title, rating);
final blogId = createdPost.blog?.id;

// Original request with mock id
final req = ModelQueries.list(
Post.classType,
where: Post.BLOG.attributeExists(value: true),
limit: _limit,
);

// Copy request with actual blog id
final copiedRequest = req.copyWith(
variables: {
...req.variables,
'filter': {
'blogID': {'attributeExists': true},
},
},
);
final res = await Amplify.API.query(request: copiedRequest).response;
final postFromResponse = res.data?.items[0];

expect(res, hasNoGraphQLErrors);
expect(postFromResponse?.blog?.id, isNotNull);
expect(postFromResponse?.blog?.id, blogId);
expect(postFromResponse?.title, title);
});

testWidgets('should fetch when document string contains tabs',
(WidgetTester tester) async {
const listBlogs = 'listBlogs';
Expand Down Expand Up @@ -220,6 +191,51 @@ void main({bool useExistingTestUser = false}) {
expect(postFromResponse?.title, title);
});

testWidgets('should return model if attribute exists',
(WidgetTester tester) async {
// Use same name to scope the query to the created model.
final name = 'Lorem Ipsum Test Sample: ${uuid()}';
final number = Random().nextInt(_max);
await addSamplePartial(
name,
number: number,
);
await addSamplePartial(name);

final existsRequest = ModelQueries.list(
Sample.classType,
where: Sample.NUMBER.attributeExists().and(Sample.NAME.eq(name)),
limit: _limit,
);

final existsResponse = await Amplify.API
.query(
request: existsRequest,
)
.response;

final existsData = existsResponse.data;
expect(existsData?.items.length, 1);
expect(existsData?.items[0]?.number, number);

final doesNotExistRequest = ModelQueries.list(
Sample.classType,
where: Sample.NUMBER
.attributeExists(exists: false)
.and(Sample.NAME.eq(name)),
limit: _limit,
);
final doesNotExistResponse = await Amplify.API
.query(
request: doesNotExistRequest,
)
.response;

final doesNotExistData = doesNotExistResponse.data;
expect(doesNotExistData?.items.length, 1);
expect(doesNotExistData?.items[0]?.number, null);
});

testWidgets('should copyWith request', (WidgetTester tester) async {
final title = 'Lorem Ipsum Test Post: ${uuid()}';
const rating = 0;
Expand Down
42 changes: 42 additions & 0 deletions packages/api/amplify_api/example/integration_test/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ final lowerCaseCache = <lowerCase>[];
final cpkParentCache = <CpkOneToOneBidirectionalParentCD>[];
final cpkExplicitChildCache = <CpkOneToOneBidirectionalChildExplicitCD>[];
final cpkImplicitChildCache = <CpkOneToOneBidirectionalChildImplicitCD>[];
final sampleCache = <Sample>[];

class TestUser {
TestUser({
Expand Down Expand Up @@ -217,6 +218,34 @@ Future<Post> addPostAndBlog(
return addPost(title, rating, createdBlog);
}

Future<Sample> addSamplePartial(String name, {int? number}) async {
const document = r'''
mutation CreatePartialSample($name: String, $number: Int) {
createSample(input: {name: $name, number: $number}) {
id
name
number
}
}
''';
final variables = <String, dynamic>{'name': name};
if (number != null) {
variables['number'] = number;
}
final request = GraphQLRequest<Sample>(
document: document,
variables: variables,
authorizationMode: APIAuthorizationType.userPools,
decodePath: 'createSample',
modelType: Sample.classType,
);
final response = await Amplify.API.mutate(request: request).response;
expect(response, hasNoGraphQLErrors);
final sampleFromResponse = response.data!;
sampleCache.add(sampleFromResponse);
return sampleFromResponse;
}

Future<Blog?> deleteBlog(Blog blog) async {
final request = ModelMutations.deleteById(
Blog.classType,
Expand Down Expand Up @@ -310,13 +339,26 @@ Future<lowerCase?> deleteLowerCase(lowerCase model) async {
return res.data;
}

Future<Sample> deleteSample(Sample sample) async {
final request = ModelMutations.deleteById(
Sample.classType,
sample.modelIdentifier,
authorizationMode: APIAuthorizationType.userPools,
);
final response = await Amplify.API.mutate(request: request).response;
expect(response, hasNoGraphQLErrors);
sampleCache.removeWhere((sampleFromCache) => sampleFromCache.id == sample.id);
return sample;
}

Future<void> deleteTestModels() async {
await Future.wait(blogCache.map(deleteBlog));
await Future.wait(postCache.map(deletePost));
await Future.wait(cpkExplicitChildCache.map(deleteCpkExplicitChild));
await Future.wait(cpkImplicitChildCache.map(deleteCpkImplicitChild));
await Future.wait(ownerOnlyCache.map(deleteOwnerOnly));
await Future.wait(lowerCaseCache.map(deleteLowerCase));
await Future.wait(sampleCache.map(deleteSample));
}

/// Wait for subscription established for given request.
Expand Down
Loading

0 comments on commit 996fbe6

Please sign in to comment.