Skip to content

Commit 8e632ad

Browse files
authored
Merge pull request #1297 from ie3-institute/mh/#1287-time-series-mapping-error-handling
TimeSeriesMappingSource getMapping() error handling + tests
2 parents be34756 + 8a125f3 commit 8e632ad

File tree

3 files changed

+136
-10
lines changed

3 files changed

+136
-10
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Fixed
1515
- Fixed SonarQube junit path issue in GitHub Actions [#1284](https://github.com/ie3-institute/PowerSystemDataModel/issues/1284)
16+
- Fixed no errors thrown in `getMapping()` in `TimeSeriesMappingSource` [#1287](https://github.com/ie3-institute/PowerSystemDataModel/issues/1287)
17+
1618
### Changed
1719
- Replaced `return this` with `return thisInstance` in CopyBuilders [#1250](https://github.com/ie3-institute/PowerSystemDataModel/issues/1250)
1820

src/main/java/edu/ie3/datamodel/io/source/TimeSeriesMappingSource.java

+6-10
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
*/
66
package edu.ie3.datamodel.io.source;
77

8-
import edu.ie3.datamodel.exceptions.FactoryException;
9-
import edu.ie3.datamodel.exceptions.SourceException;
10-
import edu.ie3.datamodel.exceptions.ValidationException;
8+
import edu.ie3.datamodel.exceptions.*;
119
import edu.ie3.datamodel.io.factory.EntityData;
1210
import edu.ie3.datamodel.io.factory.timeseries.TimeSeriesMappingFactory;
1311
import edu.ie3.datamodel.models.input.InputEntity;
1412
import edu.ie3.datamodel.models.timeseries.TimeSeries;
1513
import edu.ie3.datamodel.utils.Try;
16-
import edu.ie3.datamodel.utils.Try.*;
1714
import java.util.*;
1815
import java.util.stream.Collectors;
1916
import java.util.stream.Stream;
@@ -41,12 +38,11 @@ public void validate() throws ValidationException {
4138
* @return That mapping
4239
*/
4340
public Map<UUID, UUID> getMapping() throws SourceException {
44-
return getMappingSourceData()
45-
.map(this::createMappingEntry)
46-
.filter(Try::isSuccess)
47-
.map(t -> (Success<MappingEntry, FactoryException>) t)
48-
.map(Success::get)
49-
.collect(Collectors.toMap(MappingEntry::getAsset, MappingEntry::getTimeSeries));
41+
return Try.scanStream(getMappingSourceData().map(this::createMappingEntry), "MappingEntry")
42+
.transform(
43+
s -> s.collect(Collectors.toMap(MappingEntry::getAsset, MappingEntry::getTimeSeries)),
44+
SourceException::new)
45+
.getOrThrow();
5046
}
5147

5248
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* © 2025. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.io.source
7+
8+
import edu.ie3.datamodel.exceptions.FactoryException
9+
import edu.ie3.datamodel.exceptions.FailureException
10+
import edu.ie3.datamodel.exceptions.SourceException
11+
import spock.lang.Shared
12+
import spock.lang.Specification
13+
14+
import java.util.stream.Stream
15+
import javax.xml.transform.Source
16+
17+
class TimeSeriesMappingSourceTest extends Specification {
18+
19+
@Shared
20+
MixedMappingSource validSource
21+
22+
def setupSpec() {
23+
validSource = new MixedMappingSource(true, true)
24+
}
25+
26+
class MixedMappingSource extends TimeSeriesMappingSource {
27+
28+
private boolean validAsset
29+
private boolean validTimeSeries
30+
31+
MixedMappingSource(boolean validAsset, boolean validTimeSeries) {
32+
this.validAsset = validAsset
33+
this.validTimeSeries = validTimeSeries
34+
}
35+
36+
@Override
37+
Stream<Map<String, String>> getMappingSourceData() throws SourceException {
38+
switch (getCase()){
39+
case "VALID":
40+
return Stream.of(
41+
Map.of("asset", "b86e95b0-e579-4a80-a534-37c7a470a409", "timeSeries", "9185b8c1-86ba-4a16-8dea-5ac898e8caa5")
42+
)
43+
case "INVALID_ASSET":
44+
return Stream.of(
45+
Map.of("asset", "invalidAsset", "timeSeries", "3fbfaa97-cff4-46d4-95ba-a95665e87c26"),
46+
Map.of("asset", "c7ebcc6c-55fc-479b-aa6b-6fa82ccac6b8", "timeSeries", "3fbfaa97-cff4-46d4-95ba-a95665e87c26")
47+
)
48+
case "INVALID_TIMESERIES":
49+
return Stream.of(
50+
Map.of("asset", "90a96daa-012b-4fea-82dc-24ba7a7ab81c", "timeSeries", "invalidTimeSeries"),
51+
Map.of("asset","90a96daa-012b-4fea-82dc-24ba7a7ab81c","timeSeries","3fbfaa97-cff4-46d4-95ba-a95665e87c26")
52+
)
53+
case "INVALID_ALL":
54+
return Stream.of(
55+
Map.of("asset", "invalidAsset", "timeSeries", "invalidTimeSeries"),
56+
Map.of("asset", "invalidAsset2", "timeSeries", "invalidTimeSeries2")
57+
)
58+
}
59+
}
60+
61+
private String getCase() {
62+
if (validAsset && validTimeSeries) {
63+
return "VALID"
64+
} else if (!validAsset && validTimeSeries) {
65+
return "INVALID_ASSET"
66+
} else if (validAsset && !validTimeSeries) {
67+
return "INVALID_TIMESERIES"
68+
} else if (!validAsset && !validTimeSeries) {
69+
return "INVALID_ALL"
70+
}
71+
}
72+
73+
@Override
74+
Optional<Set<String>> getSourceFields() throws SourceException {
75+
return Optional.of(Set.of("asset", "timeSeries"))
76+
}
77+
}
78+
79+
80+
81+
def "valid mapping entries should correctly be processed"(){
82+
given: "dummy mapping source with valid data"
83+
84+
when:
85+
def actualMapping = validSource.getMapping()
86+
87+
then:
88+
actualMapping.size() == 1
89+
actualMapping.containsKey(UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409"))
90+
actualMapping.get(UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409")) == UUID.fromString("9185b8c1-86ba-4a16-8dea-5ac898e8caa5")
91+
}
92+
93+
def "should throw SourceException for invalid asset"(){
94+
given: "DummyMappingSource"
95+
validSource = new MixedMappingSource(false, true)
96+
when:
97+
validSource.getMapping()
98+
99+
then:
100+
def ex = thrown(SourceException)
101+
ex.cause.class == FailureException
102+
ex.cause.message.startsWith("1 exception(s) occurred within \"MappingEntry\" data")
103+
}
104+
105+
def "should throw SourceException for invalid timeSeries"(){
106+
given: "DummyMappingSource"
107+
validSource = new MixedMappingSource(true, false)
108+
when:
109+
validSource.getMapping()
110+
111+
then:
112+
def ex = thrown(SourceException)
113+
ex.cause.class == FailureException
114+
ex.cause.message.startsWith("1 exception(s) occurred within \"MappingEntry\" data")
115+
}
116+
117+
def "should throw SourceException for invalid timeSeries and asset"(){
118+
given: "DummyMappingSource"
119+
validSource = new MixedMappingSource(false, false)
120+
when:
121+
validSource.getMapping()
122+
123+
then:
124+
def ex = thrown(SourceException)
125+
ex.cause.class == FailureException
126+
ex.cause.message.startsWith("2 exception(s) occurred within \"MappingEntry\" data")
127+
}
128+
}

0 commit comments

Comments
 (0)