Skip to content

Commit c4b4991

Browse files
Implemented more tests for version and id
1 parent 312e80a commit c4b4991

6 files changed

Lines changed: 224 additions & 18 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
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+
* http://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+
package software.xdev.spring.data.eclipse.store.exceptions;
17+
18+
public class InvalidVersionException extends RuntimeException
19+
{
20+
public InvalidVersionException(final String message)
21+
{
22+
super(message);
23+
}
24+
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/VersionManager.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import jakarta.persistence.OptimisticLockException;
2222

2323
import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
24+
import software.xdev.spring.data.eclipse.store.exceptions.InvalidVersionException;
2425
import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier;
2526
import software.xdev.spring.data.eclipse.store.repository.support.AnnotatedFieldFinder;
2627

@@ -62,6 +63,13 @@ public void ensureSameVersion(final T workingCopy, final T original)
6263
workingCopy))
6364
{
6465
final Object workingCopyValue = fam2.getValueOfField(workingCopy);
66+
if(workingCopyValue == null)
67+
{
68+
throw new InvalidVersionException(
69+
"Trying to an existing versioned entity with an entity without version (version is null) "
70+
+ "is not permitted."
71+
);
72+
}
6573
if(!workingCopyValue.equals(originalValue))
6674
{
6775
throw new OptimisticLockException(
@@ -73,7 +81,7 @@ public void ensureSameVersion(final T workingCopy, final T original)
7381
}
7482
}
7583
}
76-
catch(final OptimisticLockException e)
84+
catch(final OptimisticLockException | InvalidVersionException e)
7785
{
7886
throw e;
7987
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/working/RecursiveWorkingCopier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ public <E> E getOrCreateObjectForDatastore(
168168
);
169169
}
170170

171-
versionManager.incrementVersion(workingCopy);
172171

173172
final Object id = idManager.getId(workingCopy);
174173
if(id != null)
@@ -178,6 +177,8 @@ public <E> E getOrCreateObjectForDatastore(
178177
final Optional<E> existingEntity = idManager.findById(id);
179178
if(existingEntity.isPresent())
180179
{
180+
versionManager.ensureSameVersion(workingCopy, existingEntity.get());
181+
versionManager.incrementVersion(workingCopy);
181182
return this.mergeValueIfNeeded(
182183
workingCopy,
183184
mergeValues,
@@ -188,6 +189,7 @@ public <E> E getOrCreateObjectForDatastore(
188189
}
189190
}
190191

192+
versionManager.incrementVersion(workingCopy);
191193
// The object to merge back is not a working copy, but a originalObject.
192194
// Therefore, we create a copy to persist this in the storage.
193195
final E objectForDatastore = this.genericCopy(workingCopy, true);

spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/version/VersionTest.java

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.context.ApplicationContext;
3131
import org.springframework.test.context.ContextConfiguration;
3232

33+
import software.xdev.spring.data.eclipse.store.exceptions.InvalidVersionException;
3334
import software.xdev.spring.data.eclipse.store.helper.TestData;
3435
import software.xdev.spring.data.eclipse.store.helper.TestUtil;
3536
import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations;
@@ -42,49 +43,55 @@ class VersionTest
4243
{
4344
public static Stream<Arguments> generateData()
4445
{
45-
return List.of(
46+
return Stream.of(
4647
new SingleTestDataset<>(
47-
name -> new VersionedEntityWithInteger(name),
48+
VersionedEntityWithInteger::new,
4849
context -> context.getBean(VersionedEntityWithIntegerRepository.class),
4950
1,
5051
2
5152
).toArguments(),
5253
new SingleTestDataset<>(
53-
name -> new VersionedEntityWithLong(name),
54+
VersionedEntityWithLong::new,
5455
context -> context.getBean(VersionedEntityWithLongRepository.class),
5556
1L,
5657
2L
5758
).toArguments(),
5859
new SingleTestDataset<>(
59-
name -> new VersionedEntityWithPrimitiveInteger(name),
60+
VersionedEntityWithPrimitiveInteger::new,
6061
context -> context.getBean(VersionedEntityWithPrimitiveIntegerRepository.class),
6162
1,
6263
2
6364
).toArguments(),
6465
new SingleTestDataset<>(
65-
name -> new VersionedEntityWithPrimitiveLong(name),
66+
VersionedEntityWithPrimitiveLong::new,
6667
context -> context.getBean(VersionedEntityWithPrimitiveLongRepository.class),
6768
1L,
6869
2L
6970
).toArguments(),
7071
new SingleTestDataset<>(
71-
name -> new VersionedEntityWithString(name),
72+
VersionedEntityWithString::new,
7273
context -> context.getBean(VersionedEntityWithStringRepository.class),
7374
null,
7475
null
7576
).toArguments(),
7677
new SingleTestDataset<>(
77-
name -> new VersionedEntityWithUuid(name),
78+
VersionedEntityWithUuid::new,
7879
context -> context.getBean(VersionedEntityWithUuidRepository.class),
7980
null,
8081
null
82+
).toArguments(),
83+
new SingleTestDataset<>(
84+
VersionedEntityWithId::new,
85+
context -> context.getBean(VersionedEntityWithIdRepository.class),
86+
1,
87+
2
8188
).toArguments()
82-
).stream();
89+
);
8390
}
8491

85-
private record SingleTestDataset<T extends VersionedEntity>(
92+
private record SingleTestDataset<T extends VersionedEntity<?>>(
8693
Function<String, T> enitityGenerator,
87-
Function<ApplicationContext, EclipseStoreRepository<T, Void>> repositoryGenerator,
94+
Function<ApplicationContext, EclipseStoreRepository<T, ?>> repositoryGenerator,
8895
Object firstVersion,
8996
Object secondVersion
9097
)
@@ -106,10 +113,10 @@ public VersionTest(final VersionTestConfiguration configuration)
106113

107114
@ParameterizedTest
108115
@MethodSource("generateData")
109-
<T extends VersionedEntity> void simpleSave(
116+
<T extends VersionedEntity<?>> void simpleSave(
110117
final SingleTestDataset<T> data, @Autowired final ApplicationContext context)
111118
{
112-
final EclipseStoreRepository<T, Void> repository = data.repositoryGenerator.apply(context);
119+
final EclipseStoreRepository<T, ?> repository = data.repositoryGenerator.apply(context);
113120
final T entity = data.enitityGenerator.apply(TestData.FIRST_NAME);
114121
repository.save(entity);
115122
TestUtil.doBeforeAndAfterRestartOfDatastore(
@@ -131,10 +138,10 @@ <T extends VersionedEntity> void simpleSave(
131138

132139
@ParameterizedTest
133140
@MethodSource("generateData")
134-
<T extends VersionedEntity> void doubleSave(
141+
<T extends VersionedEntity<?>> void doubleSave(
135142
final SingleTestDataset<T> data, @Autowired final ApplicationContext context)
136143
{
137-
final EclipseStoreRepository<T, Void> repository = data.repositoryGenerator.apply(context);
144+
final EclipseStoreRepository<T, ?> repository = data.repositoryGenerator.apply(context);
138145
final T entity = data.enitityGenerator.apply(TestData.FIRST_NAME);
139146
repository.save(entity);
140147
repository.save(repository.findAll().get(0));
@@ -157,10 +164,10 @@ <T extends VersionedEntity> void doubleSave(
157164

158165
@ParameterizedTest
159166
@MethodSource("generateData")
160-
<T extends VersionedEntity> void saveButLocked(
167+
<T extends VersionedEntity<?>> void saveButLocked(
161168
final SingleTestDataset<T> data, @Autowired final ApplicationContext context)
162169
{
163-
final EclipseStoreRepository<T, Void> repository = data.repositoryGenerator.apply(context);
170+
final EclipseStoreRepository<T, ?> repository = data.repositoryGenerator.apply(context);
164171
final T entity = data.enitityGenerator.apply(TestData.FIRST_NAME);
165172
repository.save(entity);
166173

@@ -243,4 +250,74 @@ void saveButLockedWithSameChild(@Autowired final VersionedEntityWithIntegerAndVe
243250

244251
Assertions.assertThrows(OptimisticLockException.class, () -> repository.save(secondLoadedEntry));
245252
}
253+
254+
@Test
255+
void replaceWithIdWithNull(@Autowired final VersionedEntityWithIdRepository repository)
256+
{
257+
final VersionedEntityWithId existingEntity = new VersionedEntityWithId(TestData.FIRST_NAME);
258+
repository.save(existingEntity);
259+
260+
final int existingId = repository.findAll().get(0).getId();
261+
final VersionedEntityWithId nextEntity = new VersionedEntityWithId(
262+
existingId,
263+
TestData.FIRST_NAME_ALTERNATIVE);
264+
265+
Assertions.assertThrows(InvalidVersionException.class, () -> repository.save(nextEntity));
266+
}
267+
268+
@Test
269+
void replaceWithIdWithDifferentVersion(@Autowired final VersionedEntityWithIdRepository repository)
270+
{
271+
final VersionedEntityWithId existingEntity = new VersionedEntityWithId(TestData.FIRST_NAME);
272+
repository.save(existingEntity);
273+
274+
final VersionedEntityWithId foundEntity = repository.findAll().get(0);
275+
final VersionedEntityWithId nextEntity = new VersionedEntityWithId(
276+
foundEntity.getId(),
277+
TestData.FIRST_NAME_ALTERNATIVE);
278+
nextEntity.setVersion(foundEntity.getVersion() + 1);
279+
280+
Assertions.assertThrows(OptimisticLockException.class, () -> repository.save(nextEntity));
281+
}
282+
283+
@Test
284+
void replaceWithIdWithSameVersion(@Autowired final VersionedEntityWithIdRepository repository)
285+
{
286+
final VersionedEntityWithId existingEntity = new VersionedEntityWithId(TestData.FIRST_NAME);
287+
repository.save(existingEntity);
288+
289+
final VersionedEntityWithId foundEntity = repository.findAll().get(0);
290+
final VersionedEntityWithId nextEntity = new VersionedEntityWithId(
291+
foundEntity.getId(),
292+
TestData.FIRST_NAME_ALTERNATIVE);
293+
nextEntity.setVersion(foundEntity.getVersion());
294+
repository.save(nextEntity);
295+
296+
TestUtil.doBeforeAndAfterRestartOfDatastore(
297+
this.configuration,
298+
() -> {
299+
final List<VersionedEntityWithId> allEntities = repository.findAll();
300+
Assertions.assertEquals(1, allEntities.size());
301+
Assertions.assertEquals(2, allEntities.get(0).getVersion());
302+
}
303+
);
304+
}
305+
306+
@Test
307+
void findById(@Autowired final VersionedEntityWithIdRepository repository)
308+
{
309+
final VersionedEntityWithId existingEntity = new VersionedEntityWithId(TestData.FIRST_NAME);
310+
repository.save(existingEntity);
311+
312+
final int existingId = repository.findAll().get(0).getId();
313+
314+
TestUtil.doBeforeAndAfterRestartOfDatastore(
315+
this.configuration,
316+
() -> {
317+
Assertions.assertTrue(repository.findById(existingId).isPresent());
318+
Assertions.assertEquals(1, repository.findById(existingId).get().getVersion());
319+
Assertions.assertEquals(existingId, repository.findById(existingId).get().getId());
320+
}
321+
);
322+
}
246323
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
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+
* http://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+
package software.xdev.spring.data.eclipse.store.integration.isolated.tests.version;
17+
18+
import jakarta.persistence.GeneratedValue;
19+
import jakarta.persistence.GenerationType;
20+
import jakarta.persistence.Id;
21+
import jakarta.persistence.Version;
22+
23+
24+
public class VersionedEntityWithId implements VersionedEntity<Integer>
25+
{
26+
@Version
27+
private Integer version;
28+
29+
@Id
30+
@GeneratedValue(strategy = GenerationType.AUTO)
31+
private int id;
32+
33+
private String name;
34+
35+
public VersionedEntityWithId(final String name)
36+
{
37+
this.name = name;
38+
}
39+
40+
public VersionedEntityWithId(final int id, final String name)
41+
{
42+
this.id = id;
43+
this.name = name;
44+
}
45+
46+
@Override
47+
public Integer getVersion()
48+
{
49+
return this.version;
50+
}
51+
52+
public String getName()
53+
{
54+
return this.name;
55+
}
56+
57+
public void setVersion(final int version)
58+
{
59+
this.version = version;
60+
}
61+
62+
@Override
63+
public void setName(final String name)
64+
{
65+
this.name = name;
66+
}
67+
68+
public int getId()
69+
{
70+
return this.id;
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
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+
* http://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+
package software.xdev.spring.data.eclipse.store.integration.isolated.tests.version;
17+
18+
import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository;
19+
20+
21+
public interface VersionedEntityWithIdRepository extends EclipseStoreRepository<VersionedEntityWithId, Integer>
22+
{
23+
}

0 commit comments

Comments
 (0)