Skip to content

Commit c522a80

Browse files
committed
Add smoke test for Spring GraphQL
See gh-29140
1 parent d5603f2 commit c522a80

File tree

11 files changed

+386
-0
lines changed

11 files changed

+386
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id "java"
3+
id "org.springframework.boot.conventions"
4+
}
5+
6+
description = "Spring Boot GraphQL smoke test"
7+
8+
dependencies {
9+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-graphql"))
10+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
11+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
12+
13+
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
14+
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-webflux"))
15+
testImplementation('org.springframework.graphql:spring-graphql-test')
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.springframework.graphql.data.method.annotation.Argument;
20+
import org.springframework.graphql.data.method.annotation.QueryMapping;
21+
import org.springframework.stereotype.Controller;
22+
23+
@Controller
24+
public class GreetingController {
25+
26+
private final GreetingService greetingService;
27+
28+
public GreetingController(GreetingService greetingService) {
29+
this.greetingService = greetingService;
30+
}
31+
32+
@QueryMapping
33+
public String greeting(@Argument String name) {
34+
return this.greetingService.greet(name);
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.springframework.security.access.prepost.PreAuthorize;
20+
import org.springframework.stereotype.Component;
21+
22+
@Component
23+
public class GreetingService {
24+
25+
@PreAuthorize("hasRole('ADMIN')")
26+
public String greet(String name) {
27+
return "Hello, " + name + "!";
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import java.util.Objects;
20+
21+
public class Project {
22+
23+
private String slug;
24+
25+
private String name;
26+
27+
public Project(String slug, String name) {
28+
this.slug = slug;
29+
this.name = name;
30+
}
31+
32+
public String getSlug() {
33+
return this.slug;
34+
}
35+
36+
public void setSlug(String slug) {
37+
this.slug = slug;
38+
}
39+
40+
public String getName() {
41+
return this.name;
42+
}
43+
44+
public void setName(String name) {
45+
this.name = name;
46+
}
47+
48+
@Override
49+
public boolean equals(Object o) {
50+
if (this == o) {
51+
return true;
52+
}
53+
if (o == null || getClass() != o.getClass()) {
54+
return false;
55+
}
56+
Project project = (Project) o;
57+
return this.slug.equals(project.slug);
58+
}
59+
60+
@Override
61+
public int hashCode() {
62+
return Objects.hash(this.slug);
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.Optional;
22+
23+
import org.springframework.graphql.data.method.annotation.Argument;
24+
import org.springframework.graphql.data.method.annotation.QueryMapping;
25+
import org.springframework.stereotype.Controller;
26+
27+
@Controller
28+
public class ProjectsController {
29+
30+
private final List<Project> projects;
31+
32+
public ProjectsController() {
33+
this.projects = Arrays.asList(new Project("spring-boot", "Spring Boot"),
34+
new Project("spring-graphql", "Spring GraphQL"), new Project("spring-framework", "Spring Framework"));
35+
}
36+
37+
@QueryMapping
38+
public Optional<Project> project(@Argument String slug) {
39+
return this.projects.stream().filter((project) -> project.getSlug().equals(slug)).findFirst();
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class SampleGraphQlApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(SampleGraphQlApplication.class, args);
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
22+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
23+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
24+
import org.springframework.security.core.userdetails.User;
25+
import org.springframework.security.core.userdetails.UserDetails;
26+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
27+
import org.springframework.security.web.DefaultSecurityFilterChain;
28+
29+
import static org.springframework.security.config.Customizer.withDefaults;
30+
31+
@Configuration
32+
@EnableWebSecurity
33+
@EnableGlobalMethodSecurity(prePostEnabled = true)
34+
public class SecurityConfig {
35+
36+
@Bean
37+
DefaultSecurityFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
38+
return http.csrf((csrf) -> csrf.disable())
39+
// Demonstrate that method security works
40+
// Best practice to use both for defense in depth
41+
.authorizeRequests((requests) -> requests.anyRequest().permitAll()).httpBasic(withDefaults()).build();
42+
}
43+
44+
@Bean
45+
public static InMemoryUserDetailsManager userDetailsService() {
46+
User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
47+
UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build();
48+
UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build();
49+
return new InMemoryUserDetailsManager(rob, admin);
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
query greeting($name: String!) {
2+
greeting(name: $name)
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
type Query {
2+
greeting(name: String! = "Spring"): String!
3+
project(slug: ID!): Project
4+
}
5+
6+
""" A Project in the Spring portfolio """
7+
type Project {
8+
""" Unique string id used in URLs """
9+
slug: ID!
10+
""" Project name """
11+
name: String!
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureWebGraphQlTester;
23+
import org.springframework.boot.test.context.SpringBootTest;
24+
import org.springframework.graphql.execution.ErrorType;
25+
import org.springframework.graphql.test.tester.WebGraphQlTester;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
@SpringBootTest
30+
@AutoConfigureWebGraphQlTester
31+
class GreetingControllerTests {
32+
33+
@Autowired
34+
private WebGraphQlTester graphQlTester;
35+
36+
@Test
37+
void shouldUnauthorizeAnonymousUsers() {
38+
this.graphQlTester.queryName("greeting").variable("name", "Brian").execute().errors().satisfy((errors) -> {
39+
assertThat(errors).hasSize(1);
40+
assertThat(errors.get(0).getErrorType()).isEqualTo(ErrorType.UNAUTHORIZED);
41+
});
42+
}
43+
44+
@Test
45+
void shouldGreetWithSpecificName() {
46+
this.graphQlTester.queryName("greeting").variable("name", "Brian")
47+
.httpHeaders((headers) -> headers.setBasicAuth("admin", "admin")).execute().path("greeting")
48+
.entity(String.class).isEqualTo("Hello, Brian!");
49+
}
50+
51+
@Test
52+
void shouldGreetWithDefaultName() {
53+
this.graphQlTester.query("{ greeting }").httpHeaders((headers) -> headers.setBasicAuth("admin", "admin"))
54+
.execute().path("greeting").entity(String.class).isEqualTo("Hello, Spring!");
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.graphql;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
23+
import org.springframework.graphql.test.tester.GraphQlTester;
24+
25+
@GraphQlTest(ProjectsController.class)
26+
class ProjectControllerTests {
27+
28+
@Autowired
29+
private GraphQlTester graphQlTester;
30+
31+
@Test
32+
void shouldFindSpringGraphQl() {
33+
this.graphQlTester.query("{ project(slug: \"spring-graphql\") { name } }").execute().path("project.name")
34+
.entity(String.class).isEqualTo("Spring GraphQL");
35+
}
36+
37+
@Test
38+
void shouldNotFindUnknownProject() {
39+
this.graphQlTester.query("{ project(slug: \"spring-unknown\") { name } }").execute().path("project.name")
40+
.valueDoesNotExist();
41+
}
42+
43+
}

0 commit comments

Comments
 (0)