Skip to content

Commit c3ebfbb

Browse files
author
Alex Khamutov
authoredJul 5, 2016
Merge pull request #3 from aososkov/master
Added testing tool for mutations.
2 parents 13ae61f + 273b282 commit c3ebfbb

17 files changed

+883
-353
lines changed
 

‎pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<modelVersion>4.0.0</modelVersion>
33

44
<groupId>com.toddfast.mutagen</groupId>
5-
<artifactId>spring-mutagen-cassandra</artifactId>
6-
<version>0.4.6-SNAPSHOT</version>
5+
<artifactId>mutagen-cassandra-tobox</artifactId>
6+
<version>0.4.7-SNAPSHOT</version>
77
<packaging>jar</packaging>
88

99
<name>toddfast-mutagen-cassandra</name>

‎src/main/java/com/toddfast/mutagen/cassandra/CassandraMutagen.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface CassandraMutagen {
1515
*
1616
*
1717
*/
18-
void initialize(String rootResourcePath)
18+
void initialize(String rootResourcePath, String testResourcePath)
1919
throws IOException;
2020

2121

‎src/main/java/com/toddfast/mutagen/cassandra/impl/CQLMutation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ protected void performMutation(Context context) {
171171
throw e;
172172
}
173173

174-
context.info("Successfully executed CQL mutation [{}]", fileName);
174+
context.info("Successfully executed CQL statement from mutation [{}]", fileName);
175175
context.debug("Successfully executed CQL \"{}\"", statement);
176176
}
177177

‎src/main/java/com/toddfast/mutagen/cassandra/impl/CassandraMutagenConfig.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ public class CassandraMutagenConfig {
1010

1111
private Integer start;
1212
private Integer end;
13+
private boolean enablePremutations;
1314

14-
public CassandraMutagenConfig(Integer mutation, Mode mode, Integer start, Integer end) {
15+
public CassandraMutagenConfig(Integer mutation, Mode mode, Integer start, Integer end, boolean enablePremutations) {
1516
this.mutation = mutation;
1617
this.mode = mode;
1718
this.start = start;
1819
this.end = end;
20+
this.enablePremutations = enablePremutations;
1921
}
2022

2123
public CassandraMutagenConfig() {
@@ -32,6 +34,11 @@ public CassandraMutagenConfig forceMutation(int mutation) {
3234
return this;
3335
}
3436

37+
public CassandraMutagenConfig enablePremutations() {
38+
this.enablePremutations = true;
39+
return this;
40+
}
41+
3542
public CassandraMutagenConfig forceRangeMutation(int start, int end) {
3643
if(start >= end) {
3744
throw new IllegalArgumentException("Start should be less than end! (start < end)");
@@ -48,6 +55,10 @@ public CassandraMutagenConfig forceVersion(int mutation) {
4855
return this;
4956
}
5057

58+
public boolean premutationsEnabled() {
59+
return enablePremutations;
60+
}
61+
5162
public Integer getMutation() {
5263
return mutation;
5364
}
@@ -65,7 +76,7 @@ public Integer getEnd() {
6576
}
6677

6778
public CassandraMutagenConfig copy() {
68-
return new CassandraMutagenConfig(mutation, mode, start, end);
79+
return new CassandraMutagenConfig(mutation, mode, start, end, enablePremutations);
6980
}
7081

7182
public enum Mode {

‎src/main/java/com/toddfast/mutagen/cassandra/impl/CassandraMutagenImpl.java

+124-117
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import com.toddfast.mutagen.cassandra.CassandraMutagen;
1010
import com.toddfast.mutagen.cassandra.CassandraSubject;
1111
import com.toddfast.mutagen.cassandra.dao.SchemaVersionDao;
12+
import com.toddfast.mutagen.cassandra.premutation.Premutation;
13+
import com.toddfast.mutagen.cassandra.premutation.PremutationProcessor;
1214
import org.slf4j.Logger;
1315
import org.slf4j.LoggerFactory;
1416

@@ -22,24 +24,24 @@
2224
import java.util.regex.Pattern;
2325

2426
/**
25-
*
26-
*
2727
* @author Todd Fast
2828
*/
2929
public class CassandraMutagenImpl implements CassandraMutagen {
3030

3131
private static final Logger log = LoggerFactory.getLogger(CassandraMutagenImpl.class);
3232

33-
private CassandraSubject subject;
34-
private CassandraCoordinator coordinator;
33+
private CassandraSubject subject;
34+
private CassandraCoordinator coordinator;
3535
private SchemaVersionDao schemaVersionDao;
3636
private Session session;
3737
private CassandraMutagenConfig config;
38+
private List<String> mutationResources = new ArrayList<>();
39+
private List<String> premutationResources = new ArrayList<>();
3840

3941
public CassandraMutagenImpl(Session session, CassandraMutagenConfig config) {
40-
if (Objects.isNull(session.getLoggedKeyspace())) {
41-
throw new IllegalArgumentException("Session must be started within keyspace.");
42-
}
42+
if (Objects.isNull(session.getLoggedKeyspace())) {
43+
throw new IllegalArgumentException("Session must be started within keyspace.");
44+
}
4345
this.session = session;
4446
this.config = config.copy();
4547
this.coordinator = new CassandraCoordinator(this.config);
@@ -52,133 +54,138 @@ public CassandraMutagenImpl(Session session) {
5254
}
5355

5456
/**
55-
*
56-
*
57-
*/
58-
@Override
59-
public void initialize(String rootResourcePath)
60-
throws IOException {
61-
62-
try {
63-
List<String> discoveredResources=
64-
ResourceScanner.getInstance().getResources(
65-
rootResourcePath,Pattern.compile(".*"),
66-
getClass().getClassLoader());
67-
68-
// Make sure we found some resources
69-
if (discoveredResources.isEmpty()) {
70-
throw new IllegalArgumentException("Could not find resources "+
71-
"on path \""+rootResourcePath+"\"");
72-
}
73-
74-
Collections.sort(discoveredResources,COMPARATOR);
75-
76-
resources=new ArrayList<>();
77-
78-
for (String resource: discoveredResources) {
57+
*
58+
*
59+
*/
60+
@Override
61+
public void initialize(String mutationResourcePath, String premutationResourcePath)
62+
throws IOException {
63+
64+
try {
65+
ResourceScanner resourceScanner = ResourceScanner.getInstance();
66+
List<String> mutationResources =
67+
resourceScanner.getResources(mutationResourcePath, Pattern.compile(".*"), getClass().getClassLoader());
68+
List<String> premutationResources = resourceScanner.getResources(premutationResourcePath, Pattern.compile(".*"),
69+
getClass().getClassLoader());
70+
71+
// Make sure we found some mutationResources
72+
if (mutationResources.isEmpty()) {
73+
throw new IllegalArgumentException("Could not find mutationResources " +
74+
"on path \"" + mutationResourcePath + "\"");
75+
}
76+
77+
Collections.sort(mutationResources, COMPARATOR);
78+
79+
for (String resource : mutationResources) {
7980
log.info("Found mutation resource {}", resource);
8081

81-
if (resource.endsWith(".class")) {
82-
// Remove the file path
83-
resource=resource.substring(
84-
resource.indexOf(rootResourcePath));
85-
if (resource.contains("$")) {
86-
// skip inner classes
87-
continue;
88-
}
82+
if (resource.endsWith(".class")) {
83+
// Remove the file path for mutations
84+
resource = resource.substring(
85+
resource.indexOf(mutationResourcePath));
86+
87+
if (resource.contains("$")) {
88+
// skip inner classes
89+
continue;
90+
}
8991
}
92+
this.mutationResources.add(resource);
93+
}
9094

91-
resources.add(resource);
92-
}
93-
}
94-
catch (URISyntaxException e) {
95-
throw new IllegalArgumentException("Could not find resources on "+
96-
"path \""+rootResourcePath+"\"",e);
97-
}
98-
}
99-
100-
101-
/**
102-
*
103-
*
104-
*/
105-
public List<String> getResources() {
106-
return resources;
107-
}
108-
109-
110-
/**
111-
*
112-
*
113-
*/
114-
@Override
115-
public Plan.Result<Integer> mutate() {
116-
// Do this in a VM-wide critical section. External cluster-wide
117-
// synchronization is going to have to happen in the coordinator.
118-
synchronized (System.class) {
119-
List<Mutation<Integer>> mutations = CassandraPlanner.loadMutations(session, schemaVersionDao, config, getResources());
120-
Planner<Integer> planner=
121-
new CassandraPlanner(mutations);
122-
Plan<Integer> plan=planner.getPlan(subject,coordinator);
123-
124-
// Execute the plan
125-
return plan.execute();
126-
}
127-
}
95+
for (String resource : premutationResources) {
96+
log.info("Found premutation resource {}", resource);
97+
resource = resource.substring(
98+
resource.indexOf(premutationResourcePath));
12899

100+
if (resource.contains("$")) {
101+
// skip inner classes
102+
continue;
103+
}
104+
this.premutationResources.add(resource);
105+
}
106+
} catch (URISyntaxException e) {
107+
throw new IllegalArgumentException("Could not find mutationResources on " +
108+
"path \"" + mutationResourcePath + "\"", e);
109+
}
110+
}
129111

130112

113+
/**
114+
*
115+
*
116+
*/
117+
public List<String> getMutationResources() {
118+
return mutationResources;
119+
}
131120

132-
////////////////////////////////////////////////////////////////////////////
133-
// Fields
134-
////////////////////////////////////////////////////////////////////////////
121+
public List<String> getPremutationResources() {
122+
return premutationResources;
123+
}
135124

136-
/**
137-
* Sorts by root file name, ignoring path and file extension
138-
*
139-
*/
140-
private static final Comparator<String> COMPARATOR=
141-
(path1, path2) -> {
125+
/**
126+
*
127+
*
128+
*/
129+
@Override
130+
public Plan.Result<Integer> mutate() {
131+
// Do this in a VM-wide critical section. External cluster-wide
132+
// synchronization is going to have to happen in the coordinator.
133+
synchronized (System.class) {
134+
List<Mutation<Integer>> mutations = CassandraPlanner.loadMutations(session, schemaVersionDao, config, mutationResources);
135+
List<Premutation> premutations = PremutationProcessor.loadPremutations(session, premutationResources);
136+
Planner<Integer> planner = new CassandraPlanner(mutations, premutations, session, config);
137+
Plan<Integer> plan = planner.getPlan(subject, coordinator);
138+
// Execute the plan
139+
return plan.execute();
140+
}
141+
}
142142

143-
try {
144143

145-
int index1=path1.lastIndexOf("/");
146-
int index2=path2.lastIndexOf("/");
144+
////////////////////////////////////////////////////////////////////////////
145+
// Fields
146+
////////////////////////////////////////////////////////////////////////////
147147

148-
String file1;
149-
if (index1!=-1) {
150-
file1=path1.substring(index1+1);
151-
}
152-
else {
153-
file1=path1;
154-
}
148+
/**
149+
* Sorts by root file name, ignoring path and file extension
150+
*/
151+
private static final Comparator<String> COMPARATOR =
152+
(path1, path2) -> {
155153

156-
String file2;
157-
if (index2!=-1) {
158-
file2=path2.substring(index2+1);
159-
}
160-
else {
161-
file2=path2;
162-
}
154+
try {
163155

164-
index1=file1.lastIndexOf(".");
165-
index2=file2.lastIndexOf(".");
156+
int index1 = path1.lastIndexOf("/");
157+
int index2 = path2.lastIndexOf("/");
166158

167-
if (index1 > 1) {
168-
file1=file1.substring(0,index1);
169-
}
159+
String file1;
160+
if (index1 != -1) {
161+
file1 = path1.substring(index1 + 1);
162+
} else {
163+
file1 = path1;
164+
}
170165

171-
if (index2 > 1) {
172-
file2=file2.substring(0,index2);
173-
}
166+
String file2;
167+
if (index2 != -1) {
168+
file2 = path2.substring(index2 + 1);
169+
} else {
170+
file2 = path2;
171+
}
172+
173+
index1 = file1.lastIndexOf(".");
174+
index2 = file2.lastIndexOf(".");
175+
176+
if (index1 > 1) {
177+
file1 = file1.substring(0, index1);
178+
}
174179

175-
return file1.compareTo(file2);
176-
} catch (StringIndexOutOfBoundsException e) {
177-
throw new StringIndexOutOfBoundsException(e.getMessage()+
178-
" (path1: \""+ path1 +
179-
"\", path2: \""+ path2 +"\")");
180+
if (index2 > 1) {
181+
file2 = file2.substring(0, index2);
180182
}
181-
};
182183

183-
private List<String> resources;
184+
return file1.compareTo(file2);
185+
} catch (StringIndexOutOfBoundsException e) {
186+
throw new StringIndexOutOfBoundsException(e.getMessage() +
187+
" (path1: \"" + path1 +
188+
"\", path2: \"" + path2 + "\")");
189+
}
190+
};
184191
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.toddfast.mutagen.cassandra.impl;
2+
3+
import com.datastax.driver.core.Session;
4+
import com.toddfast.mutagen.Coordinator;
5+
import com.toddfast.mutagen.MutagenException;
6+
import com.toddfast.mutagen.Mutation;
7+
import com.toddfast.mutagen.Plan;
8+
import com.toddfast.mutagen.State;
9+
import com.toddfast.mutagen.Subject;
10+
import com.toddfast.mutagen.cassandra.premutation.Premutation;
11+
import com.toddfast.mutagen.cassandra.premutation.PremutationProcessor;
12+
13+
import java.util.ArrayList;
14+
import java.util.Iterator;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.stream.Collectors;
18+
19+
public class CassandraPlan implements Plan<Integer> {
20+
private Subject<Integer> subject;
21+
private Coordinator<Integer> coordinator;
22+
private List<Mutation<Integer>> mutations;
23+
private List<Premutation> premutations;
24+
private Session session;
25+
private CassandraMutagenConfig config;
26+
27+
public CassandraPlan(Subject<Integer> subject, Coordinator<Integer> coordinator,
28+
List<Mutation<Integer>> mutations, List<Premutation> premutations,
29+
Session session, CassandraMutagenConfig config) {
30+
this.subject = subject;
31+
this.coordinator = coordinator;
32+
this.mutations = mutations;
33+
this.premutations = premutations;
34+
this.session = session;
35+
this.config = config;
36+
}
37+
38+
@Override
39+
public Subject<Integer> getSubject() {
40+
return subject;
41+
}
42+
43+
@Override
44+
public Coordinator<Integer> getCoordinator() {
45+
return coordinator;
46+
}
47+
48+
@Override
49+
public List<Mutation<Integer>> getMutations() {
50+
return mutations;
51+
}
52+
53+
public List<Premutation> getPremutations() {
54+
return premutations;
55+
}
56+
57+
@Override
58+
public Result<Integer> execute() throws MutagenException {
59+
List<Mutation<Integer>> completedMutations = new ArrayList<>();
60+
List<Mutation<Integer>> remainingMutations =
61+
new ArrayList<>(mutations);
62+
MutagenException exception = null;
63+
64+
Mutation.Context context = new CassandraContext(subject, coordinator);
65+
Map<Integer, Premutation> premutationMap = premutations.stream()
66+
.collect(Collectors.toMap(Premutation::getMutationNumber, p -> p));
67+
State<Integer> lastState = null;
68+
for (Iterator<Mutation<Integer>>
69+
i = remainingMutations.iterator(); i.hasNext(); ) {
70+
71+
final Mutation<Integer> mutation = i.next();
72+
int mutationNumber = mutation.getResultingState().getID();
73+
try {
74+
if (config.premutationsEnabled() && premutationMap.containsKey(mutationNumber)) {
75+
PremutationProcessor processor = new PremutationProcessor(session, premutationMap.get(mutationNumber));
76+
processor.execute();
77+
mutation.mutate(context);
78+
processor.check();
79+
processor.truncate();
80+
} else {
81+
mutation.mutate(context);
82+
}
83+
84+
lastState = mutation.getResultingState();
85+
86+
// Add to the completed list, remove from remaining list
87+
completedMutations.add(mutation);
88+
i.remove();
89+
} catch (RuntimeException e) {
90+
exception = new MutagenException("Exception executing " +
91+
"mutation for state \"" + mutation.getResultingState() +
92+
"\"", e);
93+
break;
94+
}
95+
}
96+
return new MutationResult<>(this, completedMutations,
97+
remainingMutations, lastState, exception);
98+
}
99+
100+
}

‎src/main/java/com/toddfast/mutagen/cassandra/impl/CassandraPlanner.java

+140-132
Original file line numberDiff line numberDiff line change
@@ -9,154 +9,162 @@
99
import com.toddfast.mutagen.basic.BasicPlanner;
1010
import com.toddfast.mutagen.cassandra.AbstractCassandraMutation;
1111
import com.toddfast.mutagen.cassandra.dao.SchemaVersionDao;
12+
import com.toddfast.mutagen.cassandra.premutation.Premutation;
1213

1314
import java.lang.reflect.Constructor;
1415
import java.lang.reflect.InvocationTargetException;
1516
import java.util.ArrayList;
1617
import java.util.Collection;
18+
import java.util.Iterator;
1719
import java.util.List;
1820

1921
/**
20-
*
2122
* @author Todd Fast
2223
*/
2324
public class CassandraPlanner extends BasicPlanner<Integer> {
2425

25-
/**
26-
*
27-
*
28-
*/
29-
protected CassandraPlanner(List<Mutation<Integer>> mutations) {
30-
super(mutations,null);
31-
}
32-
33-
34-
/**
35-
*
36-
*
37-
*/
38-
public static List<Mutation<Integer>> loadMutations(Session session, SchemaVersionDao schemaVersionDao, CassandraMutagenConfig config, Collection<String> resources) {
39-
40-
List<Mutation<Integer>> result = new ArrayList<>();
41-
42-
for (String resource: resources) {
43-
44-
// Allow .sql files because some editors have syntax highlighting
45-
// for SQL but not CQL
46-
if (resource.endsWith(".cql") || resource.endsWith(".sql")) {
47-
CQLMutation mutation = new CQLMutation(session, schemaVersionDao, resource);
48-
mutation.setConfig(config);
49-
result.add(mutation);
50-
}
51-
else
52-
if (resource.endsWith(".class")) {
53-
result.add(
54-
loadMutationClass(session, schemaVersionDao, config, resource));
55-
}
56-
else {
57-
throw new IllegalArgumentException("Unknown type for "+
58-
"mutation resource \""+resource+"\"");
59-
}
60-
}
61-
62-
return result;
63-
}
64-
65-
66-
/**
67-
*
68-
*
69-
*/
70-
private static Mutation<Integer> loadMutationClass(Session session, SchemaVersionDao schemaVersionDao, CassandraMutagenConfig config, String resource) {
71-
72-
assert resource.endsWith(".class"):
73-
"Class resource name \""+resource+"\" should end with .class";
74-
75-
int index=resource.indexOf(".class");
76-
String className=resource.substring(0,index).replace('/','.');
77-
78-
// Load the class specified by the resource
79-
Class<?> clazz=null;
80-
try {
81-
clazz=Class.forName(className);
26+
private List<Premutation> premutations;
27+
private List<Mutation<Integer>> mutations;
28+
private Session session;
29+
private CassandraMutagenConfig config;
30+
31+
/**
32+
*
33+
*
34+
*/
35+
protected CassandraPlanner(List<Mutation<Integer>> mutations, List<Premutation> premutations, Session session, CassandraMutagenConfig config) {
36+
super(mutations, null);
37+
this.mutations = mutations;
38+
this.premutations = premutations;
39+
this.session = session;
40+
this.config = config;
41+
}
42+
43+
44+
/**
45+
*
46+
*
47+
*/
48+
public static List<Mutation<Integer>> loadMutations(Session session, SchemaVersionDao schemaVersionDao, CassandraMutagenConfig config, Collection<String> resources) {
49+
50+
List<Mutation<Integer>> result = new ArrayList<>();
51+
52+
for (String resource : resources) {
53+
54+
// Allow .sql files because some editors have syntax highlighting
55+
// for SQL but not CQL
56+
if (resource.endsWith(".cql") || resource.endsWith(".sql")) {
57+
CQLMutation mutation = new CQLMutation(session, schemaVersionDao, resource);
58+
mutation.setConfig(config);
59+
result.add(mutation);
60+
} else if (resource.endsWith(".class")) {
61+
result.add(loadMutationClass(session, schemaVersionDao, config, resource));
62+
} else {
63+
throw new IllegalArgumentException("Unknown type for " +
64+
"mutation resource \"" + resource + "\"");
65+
}
66+
}
67+
68+
return result;
69+
}
70+
71+
72+
/**
73+
*
74+
*
75+
*/
76+
private static Mutation<Integer> loadMutationClass(Session session, SchemaVersionDao schemaVersionDao, CassandraMutagenConfig config, String resource) {
77+
78+
assert resource.endsWith(".class") :
79+
"Class resource name \"" + resource + "\" should end with .class";
80+
81+
int index = resource.indexOf(".class");
82+
String className = resource.substring(0, index).replace('/', '.');
83+
84+
// Load the class specified by the resource
85+
Class<?> clazz = null;
86+
try {
87+
clazz = Class.forName(className);
8288
if (!AbstractCassandraMutation.class.isAssignableFrom(clazz)) {
8389
throw new MutagenException("Class [" + resource + "] doesn't inherit AbstractCassandraMutation");
8490
}
85-
}
86-
catch (ClassNotFoundException e) {
87-
// Should never happen
88-
throw new MutagenException("Could not load mutagen class \""+
89-
resource+"\"",e);
90-
}
91-
92-
// Instantiate the class
93-
try {
94-
Constructor<?> constructor;
95-
AbstractCassandraMutation mutation=null;
96-
97-
try {
98-
// Try a constructor taking a keyspace
99-
constructor = clazz.getConstructor(Session.class, SchemaVersionDao.class);
100-
mutation = (AbstractCassandraMutation) constructor.newInstance(session, schemaVersionDao);
101-
}
102-
catch (NoSuchMethodException e) {
103-
// Wrong assumption
104-
}
105-
106-
if (mutation==null) {
107-
// Try the null constructor
108-
try {
109-
constructor=clazz.getConstructor();
110-
mutation=(AbstractCassandraMutation) constructor.newInstance();
111-
}
112-
catch (NoSuchMethodException e) {
113-
throw new MutagenException("Could not find comparible "+
114-
"constructor for class \""+className+"\"",e);
115-
}
116-
}
91+
} catch (ClassNotFoundException e) {
92+
// Should never happen
93+
throw new MutagenException("Could not load mutagen class \"" +
94+
resource + "\"", e);
95+
}
96+
97+
// Instantiate the class
98+
try {
99+
Constructor<?> constructor;
100+
AbstractCassandraMutation mutation = null;
101+
102+
try {
103+
// Try a constructor taking a keyspace
104+
constructor = clazz.getConstructor(Session.class, SchemaVersionDao.class);
105+
mutation = (AbstractCassandraMutation) constructor.newInstance(session, schemaVersionDao);
106+
} catch (NoSuchMethodException e) {
107+
// Wrong assumption
108+
}
109+
110+
if (mutation == null) {
111+
// Try the null constructor
112+
try {
113+
constructor = clazz.getConstructor();
114+
mutation = (AbstractCassandraMutation) constructor.newInstance();
115+
} catch (NoSuchMethodException e) {
116+
throw new MutagenException("Could not find comparible " +
117+
"constructor for class \"" + className + "\"", e);
118+
}
119+
}
117120

118121
mutation.setConfig(config);
119122

120-
return mutation;
121-
}
122-
catch (InstantiationException e) {
123-
throw new MutagenException("Could not instantiate class \""+
124-
className+"\"",e);
125-
}
126-
catch (InvocationTargetException e) {
127-
if (e.getTargetException() instanceof RuntimeException) {
128-
throw (RuntimeException)e.getTargetException();
129-
}
130-
else {
131-
throw new MutagenException("Exception instantiating class \""+
132-
className+"\"",e);
133-
}
134-
}
135-
catch (IllegalAccessException e) {
136-
throw new MutagenException("Could not access constructor for "+
137-
"mutation class \""+className+"\"",e);
138-
}
139-
}
140-
141-
142-
/**
143-
*
144-
*
145-
*/
146-
@Override
147-
protected Mutation.Context createContext(Subject<Integer> subject,
148-
Coordinator<Integer> coordinator) {
149-
return new CassandraContext(subject,coordinator);
150-
}
151-
152-
153-
/**
154-
*
155-
*
156-
*/
157-
@Override
158-
public Plan<Integer> getPlan(Subject<Integer> subject,
159-
Coordinator<Integer> coordinator) {
160-
return super.getPlan(subject,coordinator);
161-
}
123+
return mutation;
124+
} catch (InstantiationException e) {
125+
throw new MutagenException("Could not instantiate class \"" +
126+
className + "\"", e);
127+
} catch (InvocationTargetException e) {
128+
if (e.getTargetException() instanceof RuntimeException) {
129+
throw (RuntimeException) e.getTargetException();
130+
} else {
131+
throw new MutagenException("Exception instantiating class \"" +
132+
className + "\"", e);
133+
}
134+
} catch (IllegalAccessException e) {
135+
throw new MutagenException("Could not access constructor for " +
136+
"mutation class \"" + className + "\"", e);
137+
}
138+
}
139+
140+
141+
/**
142+
*
143+
*
144+
*/
145+
@Override
146+
protected Mutation.Context createContext(Subject<Integer> subject,
147+
Coordinator<Integer> coordinator) {
148+
return new CassandraContext(subject, coordinator);
149+
}
150+
151+
152+
/**
153+
*
154+
*
155+
*/
156+
@Override
157+
public Plan<Integer> getPlan(Subject<Integer> subject,
158+
Coordinator<Integer> coordinator) {
159+
List<Mutation<Integer>> subjectMutations = new ArrayList<>(mutations);
160+
// Filter out the mutations that are unacceptable to the subject
161+
for (Iterator<Mutation<Integer>> i = subjectMutations.iterator(); i.hasNext(); ) {
162+
Mutation<Integer> mutation = i.next();
163+
if (!coordinator.accept(subject, mutation.getResultingState())) {
164+
i.remove();
165+
}
166+
}
167+
return new CassandraPlan(subject, coordinator, subjectMutations,
168+
premutations, session, config);
169+
}
162170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.toddfast.mutagen.cassandra.impl;
2+
3+
import com.toddfast.mutagen.MutagenException;
4+
import com.toddfast.mutagen.Mutation;
5+
import com.toddfast.mutagen.Plan;
6+
import com.toddfast.mutagen.State;
7+
8+
import java.util.List;
9+
10+
public class MutationResult<I extends Comparable<I>> implements Plan.Result<I> {
11+
12+
private Plan<I> plan;
13+
private List<Mutation<I>> completedMutations;
14+
private List<Mutation<I>> remainingMutations;
15+
private State<I> lastState;
16+
private MutagenException exception;
17+
18+
19+
public MutationResult(Plan<I> plan, List<Mutation<I>> completedMutations,
20+
List<Mutation<I>> remainingMutations, State<I> lastState, MutagenException exception) {
21+
this.plan = plan;
22+
this.completedMutations = completedMutations;
23+
this.remainingMutations = remainingMutations;
24+
this.lastState = lastState;
25+
this.exception = exception;
26+
}
27+
28+
@Override
29+
public Plan<I> getPlan() {
30+
return plan;
31+
}
32+
33+
@Override
34+
public boolean isMutationComplete() {
35+
return remainingMutations.isEmpty();
36+
}
37+
38+
@SuppressWarnings("unchecked")
39+
@Override
40+
public State<I> getLastState() {
41+
return lastState;
42+
}
43+
44+
@Override
45+
public List<Mutation<I>> getCompletedMutations() {
46+
return completedMutations;
47+
}
48+
49+
@Override
50+
public List<Mutation<I>> getRemainingMutations() {
51+
return remainingMutations;
52+
}
53+
54+
@Override
55+
public MutagenException getException() {
56+
return exception;
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.toddfast.mutagen.cassandra.premutation;
2+
3+
/**
4+
* Should be thrown in Premutation.check() method in case of
5+
* check failure
6+
*/
7+
public class CheckStateException extends RuntimeException {
8+
public CheckStateException(String message) {
9+
super(message);
10+
}
11+
12+
public CheckStateException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.toddfast.mutagen.cassandra.premutation;
2+
3+
import com.datastax.driver.core.Session;
4+
5+
public abstract class Premutation {
6+
7+
private int mutationNumber;
8+
private Session session;
9+
10+
public Premutation(Session session) {
11+
this.session = session;
12+
}
13+
14+
protected void setMutationNumber(int mutationNumber) {
15+
this.mutationNumber = mutationNumber;
16+
}
17+
18+
public int getMutationNumber() {
19+
return mutationNumber;
20+
}
21+
22+
/**
23+
* Use this method to form scheme to insert
24+
* before java based class mutation
25+
*
26+
* @return formed scheme
27+
*/
28+
public abstract Scheme formScheme();
29+
30+
/**
31+
* Evaluates after java based mutation and
32+
* before truncation. Check logical dao state
33+
* in this method.
34+
* @throws CheckStateException should be thrown
35+
* in case data model is corrupted after mutation
36+
*/
37+
public abstract void check() throws CheckStateException;
38+
39+
public Session getSession() {
40+
return session;
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.toddfast.mutagen.cassandra.premutation;
2+
3+
import com.datastax.driver.core.Session;
4+
import com.datastax.driver.core.querybuilder.Insert;
5+
import com.datastax.driver.core.querybuilder.QueryBuilder;
6+
import com.toddfast.mutagen.MutagenException;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Optional;
14+
15+
public class PremutationProcessor {
16+
private final static Logger log = LoggerFactory.getLogger(PremutationProcessor.class);
17+
private Session session;
18+
private Premutation premutation;
19+
20+
public PremutationProcessor(Session session, Premutation premutation) {
21+
this.session = session;
22+
this.premutation = premutation;
23+
}
24+
25+
public void execute() {
26+
log.info("Initializing scheme for [{}] mutation", premutation.getMutationNumber());
27+
Scheme scheme = premutation.formScheme();
28+
Optional<String> keyspace = Optional.ofNullable(scheme.getKeyspace());
29+
for (Record record : scheme.getRecords()) {
30+
Insert insert = QueryBuilder.insertInto(keyspace.orElse(session.getLoggedKeyspace()), record.getTableName());
31+
for (Map.Entry<String, Object> entry : record.getFields().entrySet()) {
32+
insert.value(entry.getKey(), entry.getValue());
33+
}
34+
session.execute(insert);
35+
}
36+
}
37+
38+
public void truncate() {
39+
log.info("Cleaning scheme after [{}] mutation", premutation.getMutationNumber());
40+
Scheme scheme = premutation.formScheme();
41+
Optional<String> keyspace = Optional.ofNullable(scheme.getKeyspace());
42+
for (Record record : scheme.getRecords()) {
43+
session.execute(QueryBuilder.truncate(keyspace.orElse(session.getLoggedKeyspace()), record.getTableName()));
44+
}
45+
}
46+
47+
public void check() {
48+
log.info("Checking scheme after [{}] mutation", premutation.getMutationNumber());
49+
try {
50+
premutation.check();
51+
} catch (CheckStateException ex) {
52+
log.info("Checking scheme after [{}] mutation has failed. Cleaning scheme...", premutation.getMutationNumber());
53+
truncate();
54+
throw ex;
55+
}
56+
57+
}
58+
59+
public static List<Premutation> loadPremutations(Session session, List<String> resources) {
60+
List<Premutation> premutations = new ArrayList<>();
61+
resources.stream().filter(resource -> resource.endsWith(".class")).forEach(resource -> {
62+
int index = resource.indexOf(".class");
63+
String className = resource.substring(0, index).replace('/', '.');
64+
try {
65+
if (isPremutationResource(className)) {
66+
premutations.add(loadPremutation(session, className));
67+
}
68+
} catch (ClassNotFoundException e) {
69+
log.error("class [{}] was not found.", className);
70+
}
71+
});
72+
return premutations;
73+
}
74+
75+
public static Premutation loadPremutation(Session session, String className) {
76+
int mutationNumber;
77+
try {
78+
mutationNumber = Integer.valueOf(className.substring(className.lastIndexOf('.') + 2, className.indexOf('_')));
79+
} catch (NumberFormatException | IndexOutOfBoundsException e) {
80+
log.error("Malformed premutation class name. Class name must start with Vxxx_{name}", e);
81+
throw e;
82+
}
83+
try {
84+
Class<?> clazz = Class.forName(className);
85+
if (!isPremutationResource(className)) {
86+
throw new MutagenException("Class [" + className + "] doesn't inherit Premutation class");
87+
}
88+
Premutation premutation = (Premutation) clazz.getConstructor(Session.class).newInstance(session);
89+
premutation.setMutationNumber(mutationNumber);
90+
return premutation;
91+
} catch (ReflectiveOperationException e) {
92+
log.error("Can not instantiate premutation class", e);
93+
throw new RuntimeException(e);
94+
}
95+
}
96+
97+
public static boolean isPremutationResource(String className) throws ClassNotFoundException {
98+
return Premutation.class.isAssignableFrom(Class.forName(className));
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.toddfast.mutagen.cassandra.premutation;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class Record {
7+
private String table;
8+
private Map<String, Object> fields = new HashMap<>();
9+
10+
private Record(String table) {
11+
this.table = table;
12+
}
13+
14+
public Record value(String field, Object value) {
15+
fields.put(field, value);
16+
return this;
17+
}
18+
19+
public String getTableName() {
20+
return table;
21+
}
22+
23+
public Map<String, Object> getFields() {
24+
return fields;
25+
}
26+
27+
public static Record into(String tableName) {
28+
return new Record(tableName);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.toddfast.mutagen.cassandra.premutation;
2+
3+
import java.util.Arrays;
4+
import java.util.Collection;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
8+
public class Scheme {
9+
private String keyspace;
10+
private Set<Record> records = new HashSet<>();
11+
12+
private Scheme() {
13+
}
14+
15+
private Scheme(String keyspace) {
16+
this.keyspace = keyspace;
17+
}
18+
19+
private Scheme(String keyspace, Collection<Record> records) {
20+
this(keyspace);
21+
this.records = new HashSet<>(records);
22+
}
23+
24+
private Scheme(Collection<Record> records) {
25+
this.records = new HashSet<>(records);
26+
}
27+
28+
public void setKeyspace(String keyspace) {
29+
this.keyspace = keyspace;
30+
}
31+
32+
public Scheme addRecord(Record record) {
33+
records.add(record);
34+
return this;
35+
}
36+
37+
public Scheme addRecords(Collection<Record> record) {
38+
records.addAll(record);
39+
return this;
40+
}
41+
42+
public static Scheme instance() {
43+
return new Scheme();
44+
}
45+
46+
public static Scheme instance(String keyspace) {
47+
return new Scheme(keyspace);
48+
}
49+
50+
public static Scheme instance(Collection<Record> records) {
51+
return new Scheme(records);
52+
}
53+
54+
public static Scheme instance(String keyspace, Collection<Record> records) {
55+
return new Scheme(keyspace, records);
56+
}
57+
58+
public Scheme addRecords(Record... records) {
59+
return addRecords(Arrays.asList(records));
60+
}
61+
62+
public Set<Record> getRecords() {
63+
return records;
64+
}
65+
66+
public String getKeyspace() {
67+
return keyspace;
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,20 @@
11
package com.toddfast.mutagen.cassandra.impl;
22

3-
import com.datastax.driver.core.Cluster;
43
import com.datastax.driver.core.Row;
5-
import com.datastax.driver.core.Session;
64
import com.datastax.driver.core.querybuilder.QueryBuilder;
75
import com.datastax.driver.core.querybuilder.Select;
86
import com.toddfast.mutagen.Plan;
97
import com.toddfast.mutagen.State;
108
import com.toddfast.mutagen.cassandra.table.SchemaConstants;
11-
import org.apache.cassandra.exceptions.ConfigurationException;
12-
import org.apache.thrift.transport.TTransportException;
13-
import org.cassandraunit.AbstractCassandraUnit4TestCase;
14-
import org.cassandraunit.dataset.DataSet;
15-
import org.cassandraunit.dataset.yaml.ClassPathYamlDataSet;
16-
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
17-
import org.junit.After;
18-
import org.junit.AfterClass;
19-
import org.junit.Before;
20-
import org.junit.BeforeClass;
219
import org.junit.Test;
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
24-
25-
import java.io.IOException;
2610

2711
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
2812
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
29-
import static org.junit.Assert.*;
30-
31-
public class CassandraMutagenImplTest extends AbstractCassandraUnit4TestCase {
32-
33-
private static final Logger log = LoggerFactory.getLogger(CassandraMutagenImplTest.class);
34-
35-
private static final String KEYSPACE = "mutagen_test";
36-
37-
private static Cluster cluster;
38-
private static Session session;
39-
40-
public CassandraMutagenImplTest() {
41-
}
42-
43-
@Override
44-
public DataSet getDataSet() {
45-
return new ClassPathYamlDataSet("keyspaceDataSet.yml");
46-
}
47-
48-
@BeforeClass
49-
public static void setUpOnce() throws InterruptedException, TTransportException, ConfigurationException, IOException {
50-
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
51-
cluster = Cluster.builder().addContactPoint("127.0.0.1").withPort(9142).build();
52-
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
53-
}
54-
55-
@AfterClass
56-
public static void tearDownClass() {
57-
cluster.close();
58-
log.info("Dropped keyspace " + KEYSPACE);
59-
}
60-
61-
@Before
62-
public void setUp() throws Exception {
63-
session = cluster.connect(KEYSPACE);
64-
}
65-
66-
@After
67-
public void tearDown() throws Exception {
68-
session.close();
69-
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
70-
}
71-
72-
/**
73-
* This is it!
74-
*
75-
* @param config
76-
*/
77-
private Plan.Result<Integer> mutate(CassandraMutagenConfig config)
78-
throws IOException {
79-
80-
// Initialize the list of mutations
81-
String rootResourcePath="com/toddfast/mutagen/cassandra/test/mutations";
82-
83-
84-
CassandraMutagenImpl mutagen = new CassandraMutagenImpl(session, config);
85-
mutagen.initialize(rootResourcePath);
86-
87-
// Mutate!
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertNull;
15+
import static org.junit.Assert.assertTrue;
8816

89-
return mutagen.mutate();
90-
}
17+
public class CassandraMutagenImplTest extends MutagenBaseTest {
9118

9219
private State<Integer> testMutate(CassandraMutagenConfig config) throws Exception {
9320
Plan.Result<Integer> result = mutate(config);
@@ -109,9 +36,9 @@ private State<Integer> testMutate(CassandraMutagenConfig config) throws Exceptio
10936
return state;
11037
}
11138

112-
@Test
113-
public void testForceMutate() throws Exception {
114-
testMutate(new CassandraMutagenConfig());
39+
@Test
40+
public void testForceMutate() throws Exception {
41+
testMutate(config());
11542

11643
session.execute(
11744
QueryBuilder.insertInto("Test1")
@@ -133,7 +60,7 @@ public void testForceMutate() throws Exception {
13360
Row rowVersionBefore = session.execute(selectVersion).one();
13461
assertEquals(5, rowVersionBefore.getBytes("value").getInt());
13562

136-
State<Integer> state = testMutate(new CassandraMutagenConfig().forceMutation(3));
63+
State<Integer> state = testMutate(config().forceMutation(3));
13764
assertEquals(3, (int) state.getID());
13865

13966
Row rowAfter = session.execute(select).one();
@@ -147,7 +74,7 @@ public void testForceMutate() throws Exception {
14774

14875
@Test
14976
public void testForceRangeMutate() throws Exception {
150-
testMutate(new CassandraMutagenConfig());
77+
testMutate(config());
15178

15279
Select selectVersion = select().from(SchemaConstants.TABLE_SCHEMA_VERSION);
15380
selectVersion.where(eq("key", "state"));
@@ -156,13 +83,13 @@ public void testForceRangeMutate() throws Exception {
15683
Row rowVersionBefore = session.execute(selectVersion).one();
15784
assertEquals(5, rowVersionBefore.getBytes("value").getInt());
15885

159-
State<Integer> state = testMutate(new CassandraMutagenConfig().forceRangeMutation(3, 4));
86+
State<Integer> state = testMutate(config().forceRangeMutation(3, 4));
16087
assertEquals(4, (int) state.getID());
16188
}
16289

16390
@Test
16491
public void testForceVersion() throws Exception {
165-
testMutate(new CassandraMutagenConfig());
92+
testMutate(config());
16693

16794
session.execute(
16895
QueryBuilder.insertInto("Test1")
@@ -184,7 +111,7 @@ public void testForceVersion() throws Exception {
184111
Row rowVersionBefore = session.execute(selectVersion).one();
185112
assertEquals(5, rowVersionBefore.getBytes("value").getInt());
186113

187-
State<Integer> state = testMutate(new CassandraMutagenConfig().forceVersion(3));
114+
State<Integer> state = testMutate(config().forceVersion(3));
188115
assertEquals(3, (int) state.getID());
189116

190117
Row rowAfter = session.execute(select).one();
@@ -197,36 +124,41 @@ public void testForceVersion() throws Exception {
197124
}
198125

199126
/**
200-
*
201-
*
202-
*/
203-
@Test
204-
public void testData() throws Exception {
127+
*
128+
*
129+
*/
130+
@Test
131+
public void testData() throws Exception {
205132

206-
State<Integer> state = testMutate(new CassandraMutagenConfig());
133+
State<Integer> state = testMutate(config());
207134

208135
assertEquals(5, state != null ? (int) state.getID() : -1);
209136

210137
Select select = QueryBuilder.select().all().from("Test1");
211138
select.where(eq("key", "row1"));
212139
Row row = session.execute(select).one();
213140

214-
assertEquals("foo", row.getString("value1"));
215-
assertEquals("bar", row.getString("value2"));
141+
assertEquals("foo", row.getString("value1"));
142+
assertEquals("bar", row.getString("value2"));
216143

217144
select = QueryBuilder.select().all().from("Test1");
218145
select.where(eq("key", "row2"));
219146
row = session.execute(select).one();
220147

221-
assertEquals("chicken", row.getString("value1"));
222-
assertEquals("sneeze", row.getString("value2"));
148+
assertEquals("chicken", row.getString("value1"));
149+
assertEquals("sneeze", row.getString("value2"));
223150

224151
select = QueryBuilder.select().all().from("Test1");
225152
select.where(eq("key", "row3"));
226153
row = session.execute(select).one();
227154

228-
assertEquals("bar", row.getString("value1"));
229-
assertEquals("baz", row.getString("value2"));
155+
assertEquals("bar", row.getString("value1"));
156+
assertEquals("baz", row.getString("value2"));
230157

231-
}
158+
}
159+
160+
@Override
161+
public CassandraMutagenConfig config() {
162+
return new CassandraMutagenConfig();
163+
}
232164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.toddfast.mutagen.cassandra.impl;
2+
3+
import com.datastax.driver.core.Cluster;
4+
import com.datastax.driver.core.Session;
5+
import com.toddfast.mutagen.Plan;
6+
import org.apache.cassandra.exceptions.ConfigurationException;
7+
import org.apache.thrift.transport.TTransportException;
8+
import org.cassandraunit.AbstractCassandraUnit4TestCase;
9+
import org.cassandraunit.dataset.DataSet;
10+
import org.cassandraunit.dataset.yaml.ClassPathYamlDataSet;
11+
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
12+
import org.junit.After;
13+
import org.junit.AfterClass;
14+
import org.junit.Before;
15+
import org.junit.BeforeClass;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
18+
19+
import java.io.IOException;
20+
21+
public abstract class MutagenBaseTest extends AbstractCassandraUnit4TestCase {
22+
protected static final Logger log = LoggerFactory.getLogger(CassandraMutagenImplTest.class);
23+
24+
protected static final String KEYSPACE = "mutagen_test";
25+
26+
protected static Cluster cluster;
27+
protected static Session session;
28+
29+
@Override
30+
public DataSet getDataSet() {
31+
return new ClassPathYamlDataSet("keyspaceDataSet.yml");
32+
}
33+
34+
@BeforeClass
35+
public static void setUpOnce() throws InterruptedException, TTransportException, ConfigurationException, IOException {
36+
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
37+
cluster = Cluster.builder().addContactPoint("127.0.0.1").withPort(9142).build();
38+
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
39+
}
40+
41+
@AfterClass
42+
public static void tearDownClass() {
43+
cluster.close();
44+
log.info("Dropped keyspace " + KEYSPACE);
45+
}
46+
47+
@Before
48+
public void setUp() throws Exception {
49+
session = cluster.connect(KEYSPACE);
50+
}
51+
52+
@After
53+
public void tearDown() throws Exception {
54+
session.close();
55+
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
56+
}
57+
58+
public abstract CassandraMutagenConfig config();
59+
60+
protected Plan.Result<Integer> mutate(CassandraMutagenConfig config)
61+
throws IOException {
62+
63+
// Initialize the list of mutations
64+
String rootResourcePath = "com/toddfast/mutagen/cassandra/test/mutations";
65+
String premutationsPath = "com/toddfast/mutagen/cassandra/test/premutations";
66+
67+
68+
CassandraMutagenImpl mutagen = new CassandraMutagenImpl(session, config);
69+
mutagen.initialize(rootResourcePath, premutationsPath);
70+
71+
// Mutate!
72+
73+
return mutagen.mutate();
74+
}
75+
76+
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.toddfast.mutagen.cassandra.impl;
2+
3+
import com.toddfast.mutagen.Plan;
4+
import com.toddfast.mutagen.State;
5+
import com.toddfast.mutagen.cassandra.premutation.CheckStateException;
6+
import org.junit.Test;
7+
8+
import static junit.framework.Assert.assertFalse;
9+
import static org.junit.Assert.assertTrue;
10+
11+
public class PremutationTest extends MutagenBaseTest {
12+
13+
@Test
14+
public void testMutate() throws Exception {
15+
Plan.Result<Integer> result = mutate(config());
16+
17+
// Check the results
18+
State<Integer> state = result.getLastState();
19+
20+
log.info("Mutation complete: {}", result.isMutationComplete());
21+
log.info("Exception: {}", result.getException());
22+
if (result.getException() != null) {
23+
result.getException().printStackTrace();
24+
}
25+
log.info("Completed mutations: ", result.getCompletedMutations());
26+
log.info("Remaining mutations: ", result.getRemainingMutations());
27+
log.info("Last state: " + (state != null ? state.getID() : "null"));
28+
29+
assertFalse(result.isMutationComplete());
30+
assertTrue(result.getException().getCause() instanceof CheckStateException);
31+
}
32+
33+
@Override
34+
public CassandraMutagenConfig config() {
35+
return new CassandraMutagenConfig().enablePremutations();
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.toddfast.mutagen.cassandra.test.premutations;
2+
3+
import com.datastax.driver.core.Row;
4+
import com.datastax.driver.core.Session;
5+
import com.datastax.driver.core.querybuilder.QueryBuilder;
6+
import com.toddfast.mutagen.cassandra.premutation.CheckStateException;
7+
import com.toddfast.mutagen.cassandra.premutation.Premutation;
8+
import com.toddfast.mutagen.cassandra.premutation.Scheme;
9+
import com.toddfast.mutagen.cassandra.premutation.Record;
10+
11+
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
12+
13+
public class V003_test_premutation extends Premutation {
14+
15+
public V003_test_premutation(Session session) {
16+
super(session);
17+
}
18+
19+
@Override
20+
public Scheme formScheme() {
21+
return Scheme.instance().addRecord(
22+
Record.into("Test1")
23+
.value("key", "test")
24+
.value("value1", "val1"));
25+
//value2 is intentionally omitted
26+
27+
}
28+
29+
@Override
30+
public void check() {
31+
Session session = getSession();
32+
Row row = session.execute(QueryBuilder.select()
33+
.all()
34+
.from("Test1")
35+
.where(eq("key", "row2")))
36+
.one();
37+
String val = row.getString("value2");
38+
if (val != null) {
39+
throw new CheckStateException("field [value2] has value [" +
40+
val + "] and we'll pretend that it is wrong!");
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)
Please sign in to comment.