Skip to content

Commit 9d9a7fb

Browse files
committed
feat: add integration tests
1 parent bd64246 commit 9d9a7fb

27 files changed

+1019
-5
lines changed

.github/workflows/gradle.yml

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ name: Java CI
22

33
on:
44
push:
5-
tags-ignore:
5+
branches:
6+
- '*'
7+
tags-ignore:
68
- '*'
79

810
jobs:
@@ -16,9 +18,17 @@ jobs:
1618
uses: actions/setup-java@v1
1719
with:
1820
java-version: 1.8
19-
21+
22+
- name: Start docker compose for integration tests
23+
run: docker-compose -f docker/docker-compose.yml up -d
24+
2025
- name: Build with Gradle
21-
run: ./gradlew build
26+
run: ./gradlew build jacocoTestReport
27+
28+
- name: Upload coverage to Codecov
29+
uses: codecov/codecov-action@v1
30+
with:
31+
file: ./build/reports/jacoco/report.xml
2232

2333
- name: Upload build artifact
2434
uses: actions/upload-artifact@v1

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
.gradle/
44
build/
55
out/
6+
docker/data/
67
state.yaml
7-
plan.json
8+
plan.json

build.gradle

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ plugins {
33
id 'groovy'
44
id 'application'
55
id 'idea'
6+
id 'jacoco'
67
id 'org.inferred.processors' version '1.2.10'
78
id "net.ltgt.apt" version "0.21"
89
id 'com.github.johnrengelman.shadow' version '4.0.4'
@@ -37,7 +38,25 @@ dependencies {
3738
testCompile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.4'
3839
testCompile group: 'org.spockframework', name: 'spock-core', version: '1.0-groovy-2.4'
3940
testCompile group: 'cglib', name: 'cglib-nodep', version: '2.2'
41+
testCompile group: 'com.github.stefanbirkner', name: 'system-rules', version: '1.19.0'
42+
testCompile group: 'org.skyscreamer', name: 'jsonassert', version: '1.5.0'
43+
}
44+
45+
jacocoTestReport {
46+
reports {
47+
xml.enabled = true
48+
html.enabled = true
49+
xml.destination "${buildDir}/reports/jacoco/report.xml"
50+
}
4051

52+
afterEvaluate {
53+
classDirectories = files(classDirectories.files.collect {
54+
fileTree(dir: it,
55+
excludes: [
56+
'**/*_Builder*/**',
57+
])
58+
})
59+
}
4160
}
4261

4362
jar {

docker/config/kafka_server_jaas.conf

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
KafkaServer {
2+
org.apache.kafka.common.security.plain.PlainLoginModule required
3+
username="kafka"
4+
password="kafka-secret"
5+
user_kafka="kafka-secret"
6+
user_test="test-secret";
7+
};
8+
9+
KafkaClient {
10+
org.apache.kafka.common.security.plain.PlainLoginModule required
11+
username="test"
12+
password="test-secret";
13+
};

docker/docker-compose.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
version: '2.1'
2+
3+
services:
4+
zoo1:
5+
image: zookeeper:3.4.9
6+
hostname: zoo1
7+
ports:
8+
- "2181:2181"
9+
environment:
10+
ZOO_MY_ID: 1
11+
ZOO_PORT: 2181
12+
ZOO_SERVERS: server.1=zoo1:2888:3888
13+
volumes:
14+
- ./data/zoo1/data:/data
15+
- ./data/zoo1/datalog:/datalog
16+
17+
kafka1:
18+
image: confluentinc/cp-kafka:5.3.1
19+
hostname: kafka1
20+
ports:
21+
- "9092:9092"
22+
environment:
23+
KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka1:19092,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092
24+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:SASL_PLAINTEXT,LISTENER_DOCKER_EXTERNAL:SASL_PLAINTEXT
25+
KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
26+
KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181"
27+
KAFKA_BROKER_ID: 1
28+
KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf"
29+
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
30+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
31+
KAFKA_SASL_ENABLED_MECHANISMS: PLAIN
32+
KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: PLAIN
33+
ZOOKEEPER_SASL_ENABLED: "false"
34+
KAFKA_AUTHORIZER_CLASS_NAME: "kafka.security.auth.SimpleAclAuthorizer"
35+
KAFKA_SUPER_USERS: "User:test;User:kafka"
36+
KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: "false"
37+
volumes:
38+
- ./data/kafka1/data:/var/lib/kafka/data
39+
- ./config/kafka_server_jaas.conf:/etc/kafka/kafka_server_jaas.conf
40+
depends_on:
41+
- zoo1

src/main/java/com/devshawn/kafka/gitops/StateManager.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ private void generateServiceAcls(DesiredState.Builder desiredState, DesiredState
151151
desiredStateFile.getServices().forEach((name, service) -> {
152152
AtomicReference<Integer> index = new AtomicReference<>(0);
153153
service.getAcls(name).forEach(aclDetails -> {
154-
desiredState.putAcls(String.format("%s-%s", name, index.getAndSet(index.get() + 1)), aclDetails.build());
154+
desiredState.putAcls(String.format("%s-%s", name, index.getAndSet(index.get() + 1)), buildAclDetails(name, aclDetails));
155155
});
156156

157157
if (desiredStateFile.getCustomServiceAcls().containsKey(name)) {
@@ -166,6 +166,14 @@ private void generateServiceAcls(DesiredState.Builder desiredState, DesiredState
166166
});
167167
}
168168

169+
private AclDetails buildAclDetails(String service, AclDetails.Builder aclDetails) {
170+
try {
171+
return aclDetails.build();
172+
} catch (IllegalStateException ex) {
173+
throw new MissingConfigurationException(String.format("%s for service: %s", ex.getMessage(), service));
174+
}
175+
}
176+
169177
private List<String> getPrefixedTopicsToIgnore(DesiredStateFile desiredStateFile) {
170178
List<String> topics = new ArrayList<>();
171179
try {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.devshawn.kafka.gitops
2+
3+
import org.junit.Rule
4+
import org.junit.contrib.java.lang.system.EnvironmentVariables
5+
import picocli.CommandLine
6+
import spock.lang.Specification
7+
import spock.lang.Unroll
8+
9+
@Unroll
10+
class ApplyCommandIntegrationSpec extends Specification {
11+
12+
@Rule
13+
EnvironmentVariables environmentVariables
14+
15+
void setup() {
16+
environmentVariables.set("KAFKA_BOOTSTRAP_SERVERS", "localhost:9092")
17+
environmentVariables.set("KAFKA_SASL_JAAS_USERNAME", "test")
18+
environmentVariables.set("KAFKA_SASL_JAAS_PASSWORD", "test-secret")
19+
environmentVariables.set("KAFKA_SASL_MECHANISM", "PLAIN")
20+
environmentVariables.set("KAFKA_SECURITY_PROTOCOL", "SASL_PLAINTEXT")
21+
TestUtils.cleanUpCluster()
22+
}
23+
24+
void cleanupSpec() {
25+
TestUtils.cleanUpCluster()
26+
}
27+
28+
void 'test various successful applies - #planFile'() {
29+
setup:
30+
String file = TestUtils.getResourceFilePath("plans/${planFile}-plan.json")
31+
MainCommand mainCommand = new MainCommand()
32+
CommandLine cmd = new CommandLine(mainCommand)
33+
34+
when:
35+
int exitCode = cmd.execute("-f", file, "apply", "-p", file)
36+
37+
then:
38+
exitCode == 0
39+
40+
where:
41+
planFile << [
42+
"simple",
43+
"application-service"
44+
]
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.devshawn.kafka.gitops
2+
3+
import org.junit.ClassRule
4+
import org.junit.contrib.java.lang.system.EnvironmentVariables
5+
import org.skyscreamer.jsonassert.JSONAssert
6+
import picocli.CommandLine
7+
import spock.lang.Shared
8+
import spock.lang.Specification
9+
import spock.lang.Unroll
10+
11+
@Unroll
12+
class PlanCommandIntegrationSpec extends Specification {
13+
14+
@Shared
15+
@ClassRule
16+
EnvironmentVariables environmentVariables
17+
18+
void setupSpec() {
19+
environmentVariables.set("KAFKA_BOOTSTRAP_SERVERS", "localhost:9092")
20+
environmentVariables.set("KAFKA_SASL_JAAS_USERNAME", "test")
21+
environmentVariables.set("KAFKA_SASL_JAAS_PASSWORD", "test-secret")
22+
environmentVariables.set("KAFKA_SASL_MECHANISM", "PLAIN")
23+
environmentVariables.set("KAFKA_SECURITY_PROTOCOL", "SASL_PLAINTEXT")
24+
TestUtils.cleanUpCluster()
25+
}
26+
27+
void cleanupSpec() {
28+
TestUtils.cleanUpCluster()
29+
}
30+
31+
void 'test various valid plans - #planName'() {
32+
setup:
33+
String planOutputFile = "/tmp/plan.json"
34+
String file = TestUtils.getResourceFilePath("plans/${planName}.yaml")
35+
MainCommand mainCommand = new MainCommand()
36+
CommandLine cmd = new CommandLine(mainCommand)
37+
38+
when:
39+
int exitCode = cmd.execute("-f", file, "plan", "-o", planOutputFile)
40+
41+
then:
42+
exitCode == 0
43+
44+
when:
45+
String actualPlan = TestUtils.getFileContent(planOutputFile)
46+
String expectedPlan = TestUtils.getResourceFileContent("plans/${planName}-plan.json")
47+
48+
then:
49+
JSONAssert.assertEquals(expectedPlan, actualPlan, true)
50+
51+
where:
52+
planName << [
53+
"simple",
54+
"application-service",
55+
"kafka-connect-service",
56+
"kafka-streams-service",
57+
"topics-and-services"
58+
]
59+
}
60+
61+
void 'test various valid plans with seed - #planName'() {
62+
setup:
63+
TestUtils.cleanUpCluster()
64+
TestUtils.seedCluster()
65+
String planOutputFile = "/tmp/plan.json"
66+
String file = TestUtils.getResourceFilePath("plans/${planName}.yaml")
67+
MainCommand mainCommand = new MainCommand()
68+
CommandLine cmd = new CommandLine(mainCommand)
69+
70+
when:
71+
int exitCode
72+
if (deleteDisabled) {
73+
exitCode = cmd.execute("-f", file, "--no-delete", "plan", "-o", planOutputFile)
74+
} else {
75+
exitCode = cmd.execute("-f", file, "plan", "-o", planOutputFile)
76+
}
77+
78+
then:
79+
exitCode == 0
80+
81+
when:
82+
String actualPlan = TestUtils.getFileContent(planOutputFile)
83+
String expectedPlan = TestUtils.getResourceFileContent("plans/${planName}-plan.json")
84+
85+
then:
86+
JSONAssert.assertEquals(expectedPlan, actualPlan, true)
87+
88+
where:
89+
planName | deleteDisabled
90+
"seed-topic-modification" | false
91+
"seed-topic-modification-no-delete" | true
92+
}
93+
94+
void 'test invalid plans - #planName'() {
95+
setup:
96+
ByteArrayOutputStream err = new ByteArrayOutputStream()
97+
ByteArrayOutputStream out = new ByteArrayOutputStream()
98+
PrintStream oldErr = System.err
99+
PrintStream oldOut = System.out
100+
System.setErr(new PrintStream(err))
101+
System.setOut(new PrintStream(out))
102+
String file = TestUtils.getResourceFilePath("plans/${planName}.yaml")
103+
MainCommand mainCommand = new MainCommand()
104+
CommandLine cmd = new CommandLine(mainCommand)
105+
106+
when:
107+
int exitCode = cmd.execute("-f", file, "plan")
108+
109+
then:
110+
exitCode == 2
111+
out.toString() == TestUtils.getResourceFileContent("plans/${planName}-output.txt")
112+
113+
cleanup:
114+
System.setErr(oldErr)
115+
System.setOut(oldOut)
116+
117+
where:
118+
planName << [
119+
"invalid-missing-principal",
120+
"invalid-topic"
121+
]
122+
}
123+
}

0 commit comments

Comments
 (0)