4
4
5
5
package io .airbyte .cdk .testutils ;
6
6
7
+ import com .google .common .collect .Lists ;
7
8
import io .airbyte .commons .logging .LoggingHelper ;
8
9
import io .airbyte .commons .logging .MdcScope ;
9
10
import java .lang .reflect .InvocationTargetException ;
13
14
import java .util .concurrent .ConcurrentHashMap ;
14
15
import java .util .concurrent .ConcurrentMap ;
15
16
import java .util .concurrent .atomic .AtomicInteger ;
17
+ import java .util .function .Consumer ;
16
18
import java .util .function .Supplier ;
17
19
import java .util .stream .Stream ;
18
20
import org .apache .commons .lang3 .StringUtils ;
19
21
import org .slf4j .Logger ;
20
22
import org .slf4j .LoggerFactory ;
21
23
import org .testcontainers .containers .GenericContainer ;
22
- import org .testcontainers .containers .JdbcDatabaseContainer ;
23
24
import org .testcontainers .containers .output .OutputFrame ;
24
25
import org .testcontainers .containers .output .Slf4jLogConsumer ;
25
26
import org .testcontainers .utility .DockerImageName ;
28
29
* ContainerFactory is the companion to {@link TestDatabase} and provides it with suitable
29
30
* testcontainer instances.
30
31
*/
31
- public abstract class ContainerFactory <C extends JdbcDatabaseContainer <?>> {
32
+ public abstract class ContainerFactory <C extends GenericContainer <?>> {
32
33
33
34
static private final Logger LOGGER = LoggerFactory .getLogger (ContainerFactory .class );
34
35
35
- private record ContainerKey (Class <? extends ContainerFactory > clazz , DockerImageName imageName , List <String > methods ) {};
36
+ private record ContainerKey <C extends GenericContainer <?>> (Class <? extends ContainerFactory > clazz ,
37
+ DockerImageName imageName ,
38
+ List <NamedContainerModifier <C >> methods ) {};
36
39
37
40
private static class ContainerOrException {
38
41
@@ -67,12 +70,12 @@ GenericContainer<?> container() {
67
70
68
71
}
69
72
70
- private static final ConcurrentMap <ContainerKey , ContainerOrException > SHARED_CONTAINERS = new ConcurrentHashMap <>();
73
+ private final ConcurrentMap <ContainerKey < C > , ContainerOrException > SHARED_CONTAINERS = new ConcurrentHashMap <>();
71
74
private static final AtomicInteger containerId = new AtomicInteger (0 );
72
75
73
- private static final MdcScope .Builder getTestContainerLogMdcBuilder (DockerImageName imageName , List <String > methods ) {
76
+ private final MdcScope .Builder getTestContainerLogMdcBuilder (DockerImageName imageName , List <NamedContainerModifier < C >> containerModifiers ) {
74
77
return new MdcScope .Builder ()
75
- .setLogPrefix ("testcontainer %s (%s[%s]):" .formatted (containerId .incrementAndGet (), imageName , StringUtils .join (methods , "," )))
78
+ .setLogPrefix ("testcontainer %s (%s[%s]):" .formatted (containerId .incrementAndGet (), imageName , StringUtils .join (containerModifiers , "," )))
76
79
.setPrefixColor (LoggingHelper .Color .RED_BACKGROUND );
77
80
}
78
81
@@ -85,9 +88,21 @@ private static final MdcScope.Builder getTestContainerLogMdcBuilder(DockerImageN
85
88
/**
86
89
* Returns a shared instance of the testcontainer.
87
90
*/
88
- @ SuppressWarnings ( "unchecked" )
91
+ @ Deprecated
89
92
public final C shared (String imageName , String ... methods ) {
90
- final var containerKey = new ContainerKey (getClass (), DockerImageName .parse (imageName ), Stream .of (methods ).toList ());
93
+ return shared (imageName , (NamedContainerModifier <C >) Stream .of (methods ).map (n -> new NamedContainerModifierImpl <C >(n , resolveModifierByName (n ))).toList ());
94
+ }
95
+
96
+ public final C shared (String imageName , NamedContainerModifier <C >... namedContainerModifiers ) {
97
+ return shared (imageName , List .of (namedContainerModifiers ));
98
+ }
99
+
100
+ public final C shared (String imageName ) {
101
+ return shared (imageName , new ArrayList <>());
102
+ }
103
+
104
+ public final C shared (String imageName , List <NamedContainerModifier <C >> namedContainerModifiers ) {
105
+ final ContainerKey <C > containerKey = new ContainerKey <>(getClass (), DockerImageName .parse (imageName ), namedContainerModifiers );
91
106
// We deliberately avoid creating the container itself eagerly during the evaluation of the map
92
107
// value.
93
108
// Container creation can be exceedingly slow.
@@ -102,39 +117,73 @@ public final C shared(String imageName, String... methods) {
102
117
* Returns an exclusive instance of the testcontainer.
103
118
*/
104
119
@ SuppressWarnings ("unchecked" )
120
+ @ Deprecated
105
121
public final C exclusive (String imageName , String ... methods ) {
106
- return (C ) createAndStartContainer (DockerImageName .parse (imageName ), Stream .of (methods ).toList ());
122
+ return exclusive (imageName , (NamedContainerModifier <C >) Stream .of (methods ).map (n -> new NamedContainerModifierImpl <C >(n , resolveModifierByName (n ))).toList ());
123
+ }
124
+
125
+ public final C exclusive (String imageName ) {
126
+ return exclusive (imageName , new ArrayList <>());
127
+ }
128
+
129
+ public final C exclusive (String imageName , NamedContainerModifier <C >... namedContainerModifiers ) {
130
+ return exclusive (imageName , List .of (namedContainerModifiers ));
131
+ }
132
+
133
+ public final C exclusive (String imageName , List <NamedContainerModifier <C >> namedContainerModifiers ) {
134
+ return (C ) createAndStartContainer (DockerImageName .parse (imageName ), namedContainerModifiers );
107
135
}
108
136
109
- private GenericContainer <?> createAndStartContainer (DockerImageName imageName , List <String > methodNames ) {
110
- LOGGER .info ("Creating new shared container based on {} with {}." , imageName , methodNames );
111
- try {
112
- GenericContainer <?> container = createNewContainer (imageName );
113
- final var methods = new ArrayList <Method >();
114
- for (String methodName : methodNames ) {
115
- methods .add (getClass ().getMethod (methodName , container .getClass ()));
137
+ public interface NamedContainerModifier <C extends GenericContainer <?>> {
138
+
139
+ String name ();
140
+ Consumer <C > modifier ();
141
+ }
142
+
143
+ public record NamedContainerModifierImpl <C extends GenericContainer <?>>(String name , Consumer <C > method ) implements NamedContainerModifier <C >{
144
+ public String name () {
145
+ return name ;
146
+ }
147
+ public Consumer <C > modifier () {
148
+ return method ;
149
+ }
150
+ }
151
+
152
+ private Consumer <C > resolveModifierByName (String methodName ) {
153
+ final ContainerFactory <C > self = this ;
154
+ Consumer <C > resolvedMethod = c -> {
155
+ try {
156
+ Class <? extends GenericContainer > containerClass = c .getClass ();
157
+ Method method = self .getClass ().getMethod (methodName , containerClass );
158
+ method .invoke (self , c );
159
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) {
160
+ throw new RuntimeException (e );
116
161
}
117
- final var logConsumer = new Slf4jLogConsumer (LOGGER ) {
162
+ };
163
+ return resolvedMethod ;
164
+ }
118
165
119
- public void accept (OutputFrame frame ) {
120
- if (frame .getUtf8StringWithoutLineEnding ().trim ().length () > 0 ) {
121
- super .accept (frame );
122
- }
123
- }
166
+ private C createAndStartContainer (DockerImageName imageName , List <NamedContainerModifier <C >> namedContainerModifiers ) {
167
+ LOGGER .info ("Creating new shared container based on {} with {}." , imageName , Lists .transform (namedContainerModifiers , c ->c .name ()));
168
+ C container = createNewContainer (imageName );
169
+ final var logConsumer = new Slf4jLogConsumer (LOGGER ) {
124
170
125
- };
126
- getTestContainerLogMdcBuilder (imageName , methodNames ).produceMappings (logConsumer ::withMdc );
127
- container .withLogConsumer (logConsumer );
128
- for (Method method : methods ) {
129
- LOGGER .info ("Calling {} in {} on new shared container based on {}." ,
130
- method .getName (), getClass ().getName (), imageName );
131
- method .invoke (this , container );
171
+ public void accept (OutputFrame frame ) {
172
+ if (frame .getUtf8StringWithoutLineEnding ().trim ().length () > 0 ) {
173
+ super .accept (frame );
174
+ }
132
175
}
133
- container .start ();
134
- return container ;
135
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
136
- throw new RuntimeException (e );
176
+
177
+ };
178
+ getTestContainerLogMdcBuilder (imageName , namedContainerModifiers ).produceMappings (logConsumer ::withMdc );
179
+ container .withLogConsumer (logConsumer );
180
+ for (NamedContainerModifier <C > resolvedNamedContainerModifier : namedContainerModifiers ) {
181
+ LOGGER .info ("Calling {} in {} on new shared container based on {}." ,
182
+ resolvedNamedContainerModifier .name (), getClass ().getName (), imageName );
183
+ resolvedNamedContainerModifier .modifier ().accept (container );
137
184
}
185
+ container .start ();
186
+ return container ;
138
187
}
139
188
140
189
}
0 commit comments