Skip to content

Commit e234cec

Browse files
committed
Add more test coverage
1 parent 46c8a6f commit e234cec

5 files changed

Lines changed: 281 additions & 4 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.linkedin.hoptimator;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
import static org.junit.jupiter.api.Assertions.assertNull;
8+
import static org.junit.jupiter.api.Assertions.assertSame;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
12+
13+
class PendingDeleteTest {
14+
15+
@Test
16+
void singleArgConstructorLeavesSelfOwnerNull() {
17+
Object target = new Object();
18+
PendingDelete<Object> pd = new PendingDelete<>(target);
19+
20+
assertSame(target, pd.target());
21+
assertNull(pd.selfOwnerKind());
22+
assertNull(pd.selfOwnerName());
23+
}
24+
25+
@Test
26+
void threeArgConstructorStoresSelfOwner() {
27+
Object target = new Object();
28+
PendingDelete<Object> pd = new PendingDelete<>(target, "LogicalTable", "my-table");
29+
30+
assertSame(target, pd.target());
31+
assertEquals("LogicalTable", pd.selfOwnerKind());
32+
assertEquals("my-table", pd.selfOwnerName());
33+
}
34+
35+
@Test
36+
void threeArgConstructorAcceptsNullSelfOwner() {
37+
Object target = new Object();
38+
PendingDelete<Object> pd = new PendingDelete<>(target, null, null);
39+
40+
assertNull(pd.selfOwnerKind());
41+
assertNull(pd.selfOwnerName());
42+
}
43+
44+
@Test
45+
void nullTargetThrows() {
46+
assertThrows(NullPointerException.class, () -> new PendingDelete<>(null));
47+
assertThrows(NullPointerException.class,
48+
() -> new PendingDelete<>(null, "LogicalTable", "my-table"));
49+
}
50+
51+
@Test
52+
void toStringIncludesTargetAndSelfOwnerWhenPresent() {
53+
PendingDelete<String> pd = new PendingDelete<>("the-target", "LogicalTable", "my-table");
54+
String s = pd.toString();
55+
assertTrue(s.contains("the-target"), "toString should include target: " + s);
56+
assertTrue(s.contains("LogicalTable/my-table"), "toString should include kind/name: " + s);
57+
}
58+
59+
@Test
60+
void toStringOmitsSelfOwnerWhenNull() {
61+
PendingDelete<String> pd = new PendingDelete<>("the-target");
62+
String s = pd.toString();
63+
assertTrue(s.contains("the-target"));
64+
assertFalse(s.contains("self="), "toString should not include self= when not set: " + s);
65+
}
66+
67+
@Test
68+
void toStringOmitsSelfOwnerWhenOnlyOneFieldSet() {
69+
// The toString contract says self= appears only when both kind and name are non-null.
70+
// (The K8sPipelineDependencyChecker.isSelfOwned guard also requires both.)
71+
PendingDelete<String> kindOnly = new PendingDelete<>("t", "LogicalTable", null);
72+
assertFalse(kindOnly.toString().contains("self="));
73+
74+
PendingDelete<String> nameOnly = new PendingDelete<>("t", null, "my-table");
75+
assertFalse(nameOnly.toString().contains("self="));
76+
}
77+
78+
@Test
79+
void targetGenericTypeIsPreserved() {
80+
Source source = new Source("db", java.util.List.of("schema", "tbl"), java.util.Map.of());
81+
PendingDelete<Source> pd = new PendingDelete<>(source);
82+
Source unwrapped = pd.target();
83+
assertEquals("tbl", unwrapped.table());
84+
}
85+
}

hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/PipelineDependencyLabels.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.LinkedHashSet;
99
import java.util.Map;
1010
import java.util.Set;
11-
import java.util.stream.Collectors;
1211

1312
import com.linkedin.hoptimator.Sink;
1413
import com.linkedin.hoptimator.Source;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.linkedin.hoptimator.k8s;
2+
3+
import java.sql.Connection;
4+
import java.sql.SQLException;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.ExtendWith;
10+
import org.mockito.ArgumentCaptor;
11+
import org.mockito.Mock;
12+
import org.mockito.MockedStatic;
13+
import org.mockito.junit.jupiter.MockitoExtension;
14+
15+
import com.linkedin.hoptimator.Source;
16+
import com.linkedin.hoptimator.Validator;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertFalse;
20+
import static org.junit.jupiter.api.Assertions.assertNull;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
import static org.mockito.ArgumentMatchers.eq;
23+
import static org.mockito.ArgumentMatchers.nullable;
24+
25+
26+
/**
27+
* Unit tests for {@link K8sPipelineDependencyValidator}. The validator is a thin adapter that
28+
* forwards to {@link PipelineDependencyChecker#assertNoExternalDependents} — these tests use
29+
* {@link MockedStatic} on both K8sContext and PipelineDependencyChecker to verify that source
30+
* fields and self-owner fields are forwarded correctly, and that thrown SQLException becomes a
31+
* validation issue rather than propagating.
32+
*/
33+
@ExtendWith(MockitoExtension.class)
34+
class K8sPipelineDependencyValidatorTest {
35+
36+
@Mock
37+
private MockedStatic<K8sContext> contextStatic;
38+
39+
@Mock
40+
private MockedStatic<PipelineDependencyChecker> checkerStatic;
41+
42+
@Mock
43+
private Connection connection;
44+
45+
@Mock
46+
private K8sContext context;
47+
48+
private static Source source() {
49+
return new Source("kafka1", List.of("KAFKA", "my-topic"), Map.of());
50+
}
51+
52+
@Test
53+
void validateForwardsSourceFieldsAndSelfOwnerToChecker() {
54+
contextStatic.when(() -> K8sContext.create(connection)).thenReturn(context);
55+
56+
K8sPipelineDependencyValidator validator =
57+
new K8sPipelineDependencyValidator(source(), "LogicalTable", "my-table");
58+
Validator.Issues issues = new Validator.Issues("test");
59+
60+
validator.validate(issues, connection);
61+
62+
ArgumentCaptor<String> dbCaptor = ArgumentCaptor.forClass(String.class);
63+
@SuppressWarnings("unchecked")
64+
ArgumentCaptor<List<String>> pathCaptor = ArgumentCaptor.forClass(List.class);
65+
ArgumentCaptor<String> kindCaptor = ArgumentCaptor.forClass(String.class);
66+
ArgumentCaptor<String> nameCaptor = ArgumentCaptor.forClass(String.class);
67+
68+
checkerStatic.verify(() -> PipelineDependencyChecker.assertNoExternalDependents(
69+
eq(context), dbCaptor.capture(), pathCaptor.capture(),
70+
kindCaptor.capture(), nameCaptor.capture()));
71+
72+
assertEquals("kafka1", dbCaptor.getValue());
73+
assertEquals(List.of("KAFKA", "my-topic"), pathCaptor.getValue());
74+
assertEquals("LogicalTable", kindCaptor.getValue());
75+
assertEquals("my-table", nameCaptor.getValue());
76+
assertTrue(issues.valid());
77+
}
78+
79+
@Test
80+
@SuppressWarnings("unchecked")
81+
void validatePassesNullSelfOwnerWhenUnset() {
82+
contextStatic.when(() -> K8sContext.create(connection)).thenReturn(context);
83+
84+
K8sPipelineDependencyValidator validator =
85+
new K8sPipelineDependencyValidator(source(), null, null);
86+
Validator.Issues issues = new Validator.Issues("test");
87+
88+
validator.validate(issues, connection);
89+
90+
ArgumentCaptor<String> kindCaptor = ArgumentCaptor.forClass(String.class);
91+
ArgumentCaptor<String> nameCaptor = ArgumentCaptor.forClass(String.class);
92+
checkerStatic.verify(() -> PipelineDependencyChecker.assertNoExternalDependents(
93+
eq(context), nullable(String.class), nullable(List.class),
94+
kindCaptor.capture(), nameCaptor.capture()));
95+
96+
assertNull(kindCaptor.getValue());
97+
assertNull(nameCaptor.getValue());
98+
}
99+
100+
@Test
101+
@SuppressWarnings("unchecked")
102+
void validateRecordsCheckerSqlExceptionAsIssue() {
103+
contextStatic.when(() -> K8sContext.create(connection)).thenReturn(context);
104+
checkerStatic.when(() -> PipelineDependencyChecker.assertNoExternalDependents(
105+
nullable(K8sContext.class), nullable(String.class), nullable(List.class),
106+
nullable(String.class), nullable(String.class)))
107+
.thenThrow(new SQLException("3 active pipeline(s) depend on it: p1, p2, p3"));
108+
109+
K8sPipelineDependencyValidator validator =
110+
new K8sPipelineDependencyValidator(source(), null, null);
111+
Validator.Issues issues = new Validator.Issues("test");
112+
113+
validator.validate(issues, connection);
114+
115+
assertFalse(issues.valid(), "blocking pipelines should surface as a validation error");
116+
assertTrue(issues.toString().contains("3 active pipeline"),
117+
"issue message should include the SQLException's text: " + issues);
118+
}
119+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.linkedin.hoptimator.k8s;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.Map;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import com.linkedin.hoptimator.PendingDelete;
10+
import com.linkedin.hoptimator.Source;
11+
import com.linkedin.hoptimator.Validator;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
17+
18+
class K8sValidatorProviderTest {
19+
20+
private static Source testSource() {
21+
return new Source("kafka1", java.util.List.of("KAFKA", "my-topic"), Map.of());
22+
}
23+
24+
@Test
25+
void returnsDependencyValidatorForPendingDeleteOfSource() {
26+
K8sValidatorProvider provider = new K8sValidatorProvider();
27+
PendingDelete<Source> pd = new PendingDelete<>(testSource());
28+
29+
Collection<Validator> validators = provider.validators(pd, null);
30+
31+
assertEquals(1, validators.size());
32+
assertInstanceOf(K8sPipelineDependencyValidator.class, validators.iterator().next());
33+
}
34+
35+
@Test
36+
void returnsDependencyValidatorWhenSelfOwnerIsSet() {
37+
// Self-owner fields are stored on the validator; this exercises the (kind, name) plumbing
38+
// through the provider boundary.
39+
K8sValidatorProvider provider = new K8sValidatorProvider();
40+
PendingDelete<Source> pd = new PendingDelete<>(testSource(), "LogicalTable", "my-table");
41+
42+
Collection<Validator> validators = provider.validators(pd, null);
43+
44+
assertEquals(1, validators.size());
45+
assertInstanceOf(K8sPipelineDependencyValidator.class, validators.iterator().next());
46+
}
47+
48+
@Test
49+
void returnsEmptyForRawSourceWithoutPendingDeleteWrapper() {
50+
// The validator only fires for a delete-intent signal — not for a plain Source. Other callers
51+
// of ValidationService.validate(source, ...) must NOT trigger the K8s pipeline lookup.
52+
K8sValidatorProvider provider = new K8sValidatorProvider();
53+
54+
Collection<Validator> validators = provider.validators(testSource(), null);
55+
56+
assertTrue(validators.isEmpty());
57+
}
58+
59+
@Test
60+
void returnsEmptyForPendingDeleteOfNonSourceTarget() {
61+
K8sValidatorProvider provider = new K8sValidatorProvider();
62+
PendingDelete<String> pd = new PendingDelete<>("not-a-source");
63+
64+
Collection<Validator> validators = provider.validators(pd, null);
65+
66+
assertTrue(validators.isEmpty());
67+
}
68+
69+
@Test
70+
void returnsEmptyForUnrelatedTypes() {
71+
K8sValidatorProvider provider = new K8sValidatorProvider();
72+
73+
assertTrue(provider.validators("just-a-string", null).isEmpty());
74+
assertTrue(provider.validators(42, null).isEmpty());
75+
assertTrue(provider.validators(Collections.emptyList(), null).isEmpty());
76+
}
77+
}

hoptimator-k8s/src/test/java/com/linkedin/hoptimator/k8s/PipelineDependencyLabelsTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.util.Arrays;
44
import java.util.Collections;
5-
import java.util.LinkedHashMap;
6-
import java.util.List;
75
import java.util.Map;
86
import java.util.Set;
97

@@ -15,7 +13,6 @@
1513
import static org.junit.jupiter.api.Assertions.assertEquals;
1614
import static org.junit.jupiter.api.Assertions.assertFalse;
1715
import static org.junit.jupiter.api.Assertions.assertNotEquals;
18-
import static org.junit.jupiter.api.Assertions.assertNotNull;
1916
import static org.junit.jupiter.api.Assertions.assertTrue;
2017

2118

0 commit comments

Comments
 (0)