Przeglądaj źródła

Added test for cargo loaded onto wrong voyage, fixed a few uncovered bugs.

peter_backlund 16 lat temu
rodzic
commit
d4765cdb45

+ 39
- 33
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Cargo.java Wyświetl plik

@@ -120,12 +120,6 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
120 120
     return itinerary.activitySucceding(delivery.mostRecentPhysicalHandlingActivity());
121 121
   }
122 122
 
123
-  private boolean unloadedInCustomsClearancePoint() {
124
-    return mostRecentHandlingActivity() != null &&
125
-           mostRecentHandlingActivity().location().sameAs(customsClearancePoint()) &&
126
-           mostRecentHandlingActivity().type() == UNLOAD;
127
-  }
128
-
129 123
   /**
130 124
    * @return True if cargo is misdirected.
131 125
    */
@@ -162,6 +156,29 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
162 156
   }
163 157
 
164 158
   /**
159
+   * Updates all aspects of the cargo aggregate status
160
+   * based on the current route specification, itinerary and handling of the cargo.
161
+   * <p/>
162
+   * When either of those three changes, i.e. when a new route is specified for the cargo,
163
+   * the cargo is assigned to a route or when the cargo is handled, the status must be
164
+   * re-calculated.
165
+   * <p/>
166
+   * {@link RouteSpecification} and {@link Itinerary} are both inside the Cargo
167
+   * aggregate, so changes to them cause the status to be updated <b>synchronously</b>,
168
+   * but handling cause the status update to happen <b>asynchronously</b>
169
+   * since {@link HandlingEvent} is in a different aggregate.
170
+   *
171
+   * @param handlingActivity handling activity
172
+   */
173
+  public void handled(final HandlingActivity handlingActivity) {
174
+    Validate.notNull(handlingActivity, "Handling activity is required");
175
+
176
+    if (succedsMostRecentActivity(handlingActivity)) {
177
+      this.delivery = delivery.onHandling(handlingActivity);
178
+    }
179
+  }
180
+
181
+  /**
165 182
    * Specifies a new route for this cargo.
166 183
    *
167 184
    * @param routeSpecification route specification.
@@ -222,10 +239,15 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
222 239
     return delivery.mostRecentHandlingActivity();
223 240
   }
224 241
 
242
+  /**
243
+   * @return The earliest rerouting location.
244
+   * If the cargo is in port, it's the current location.
245
+   * If it's onboard a carrier it's the next arrival location.
246
+   */
225 247
   public Location earliestReroutingLocation() {
226 248
     if (isMisdirected()) {
227 249
       if (transportStatus() == ONBOARD_CARRIER) {
228
-        return currentVoyage().nextArrivalLocation(lastKnownLocation());
250
+        return currentVoyage().arrivalLocationAfterDepartureFrom(lastKnownLocation());
229 251
       } else {
230 252
         return lastKnownLocation();
231 253
       }
@@ -235,32 +257,14 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
235 257
   }
236 258
 
237 259
   /**
238
-   * Updates all aspects of the cargo aggregate status
239
-   * based on the current route specification, itinerary and handling of the cargo.
240
-   * <p/>
241
-   * When either of those three changes, i.e. when a new route is specified for the cargo,
242
-   * the cargo is assigned to a route or when the cargo is handled, the status must be
243
-   * re-calculated.
244
-   * <p/>
245
-   * {@link RouteSpecification} and {@link Itinerary} are both inside the Cargo
246
-   * aggregate, so changes to them cause the status to be updated <b>synchronously</b>,
247
-   * but handling cause the status update to happen <b>asynchronously</b>
248
-   * since {@link HandlingEvent} is in a different aggregate.
249
-   *
250
-   * @param handlingActivity handling activity
260
+   * @param other itinerary
261
+   * @return An merge between the current itinerary and the provided itinerary
262
+   * that describes a continuous route even if the cargo is currently misdirected.
251 263
    */
252
-  public void handled(final HandlingActivity handlingActivity) {
253
-    Validate.notNull(handlingActivity, "Handling activity is required");
254
-
255
-    if (isSignificant(handlingActivity)) {
256
-      this.delivery = delivery.onHandling(handlingActivity);
257
-    }
258
-  }
259
-
260 264
   public Itinerary itineraryMergedWith(final Itinerary other) {
261 265
     if (isMisdirected() && transportStatus() == ONBOARD_CARRIER) {
262 266
       final Leg currentLeg = Leg.deriveLeg(
263
-        currentVoyage(), lastKnownLocation(), currentVoyage().nextArrivalLocation(lastKnownLocation())
267
+        currentVoyage(), lastKnownLocation(), currentVoyage().arrivalLocationAfterDepartureFrom(lastKnownLocation())
264 268
       );
265 269
 
266 270
       return this.itinerary().
@@ -274,10 +278,6 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
274 278
     }
275 279
   }
276 280
 
277
-  private boolean isSignificant(final HandlingActivity newHandlingActivity) {
278
-    return succedsMostRecentActivity(newHandlingActivity);
279
-  }
280
-
281 281
   private boolean succedsMostRecentActivity(final HandlingActivity newHandlingActivity) {
282 282
     if (delivery.hasBeenHandled()) {
283 283
       final HandlingActivity priorActivity = itinerary.strictlyPriorOf(delivery.mostRecentPhysicalHandlingActivity(), newHandlingActivity);
@@ -287,6 +287,12 @@ public class Cargo extends EntitySupport<Cargo,TrackingId> {
287 287
     }
288 288
   }
289 289
 
290
+  private boolean unloadedInCustomsClearancePoint() {
291
+    return mostRecentHandlingActivity() != null &&
292
+           mostRecentHandlingActivity().location().sameAs(customsClearancePoint()) &&
293
+           mostRecentHandlingActivity().type() == UNLOAD;
294
+  }
295
+
290 296
   @Override
291 297
   public String toString() {
292 298
     return trackingId + " (" + routeSpecification + ")";

+ 1
- 0
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Itinerary.java Wyświetl plik

@@ -259,6 +259,7 @@ public class Itinerary extends ValueObjectSupport<Itinerary> {
259 259
 
260 260
   Itinerary withLeg(final Leg leg) {
261 261
     final List<Leg> newLegs = new ArrayList<Leg>(legs.size() + 1);
262
+    newLegs.addAll(legs);
262 263
     newLegs.add(leg);
263 264
 
264 265
     return new Itinerary(newLegs);

+ 14
- 16
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/Voyage.java Wyświetl plik

@@ -67,23 +67,11 @@ public class Voyage extends EntitySupport<Voyage,VoyageNumber> {
67 67
     this.schedule = new Schedule(carrierMovements);
68 68
   }
69 69
 
70
-  @Override
71
-  public String toString() {
72
-    return voyageNumber.stringValue();
73
-  }
74
-
75
-  Voyage() {
76
-    // Needed by Hibernate
77
-    voyageNumber = null;
78
-  }
79 70
 
80
-  public Location nextArrivalLocation(final Location location) {
81
-    for (Iterator<CarrierMovement> it = schedule.carrierMovements().iterator(); it.hasNext();) {
82
-      CarrierMovement carrierMovement = it.next();
83
-      if (carrierMovement.arrivalLocation().sameAs(location)) {
84
-        return location;
85
-      } else if (carrierMovement.departureLocation().sameAs(location)) {
86
-        return it.next().arrivalLocation();
71
+  public Location arrivalLocationAfterDepartureFrom(final Location departureLocation) {
72
+    for (CarrierMovement carrierMovement : schedule.carrierMovements()) {
73
+      if (carrierMovement.departureLocation().sameAs(departureLocation)) {
74
+        return carrierMovement.arrivalLocation();
87 75
       }
88 76
     }
89 77
 
@@ -105,6 +93,16 @@ public class Voyage extends EntitySupport<Voyage,VoyageNumber> {
105 93
 
106 94
     return unmodifiableList(locations);
107 95
   }
96
+  
97
+  @Override
98
+  public String toString() {
99
+    return voyageNumber.stringValue();
100
+  }
101
+
102
+  Voyage() {
103
+    // Needed by Hibernate
104
+    voyageNumber = null;
105
+  }
108 106
 
109 107
   /**
110 108
    * Builder pattern is used for incremental construction

+ 84
- 0
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/scenario/CargoLifecycle.java Wyświetl plik

@@ -114,6 +114,8 @@ public class CargoLifecycle {
114 114
     assertNull(cargo.nextExpectedActivity());
115 115
   }
116 116
 
117
+  // TODO misdirected cargo, loaded onto wrong voyage
118
+                                                      
117 119
   @Test
118 120
   public void cargoIsMisdirectedAndRerouted() throws Exception {
119 121
 
@@ -187,6 +189,88 @@ public class CargoLifecycle {
187 189
   }
188 190
 
189 191
   @Test
192
+  public void cargoIsLoadedOntoWrongVoyage() throws Exception {
193
+
194
+    Cargo cargo = setupCargoFromHongkongToStockholm();
195
+
196
+    // Initial state, before routing
197
+    assertThat(cargo.transportStatus(), is(NOT_RECEIVED));
198
+    assertThat(cargo.routingStatus(), is(NOT_ROUTED));
199
+    assertFalse(cargo.isMisdirected());
200
+    assertNull(cargo.estimatedTimeOfArrival());
201
+    assertNull(cargo.nextExpectedActivity());
202
+
203
+    // Route: Hongkong - Long Beach - New York - Stockholm
204
+    List<Itinerary> itineraries = routingService.fetchRoutesForSpecification(cargo.routeSpecification());
205
+    Itinerary itinerary = selectAppropriateRoute(itineraries);
206
+    cargo.assignToRoute(itinerary);
207
+
208
+    // Routed
209
+    assertThat(cargo.transportStatus(), is(NOT_RECEIVED));
210
+    assertThat(cargo.routingStatus(), is(ROUTED));
211
+    assertThat(cargo.nextExpectedActivity(), is(receiveIn(HONGKONG)));
212
+    assertThat(cargo.estimatedTimeOfArrival(), is(toDate("2009-03-26")));
213
+
214
+    // Received
215
+    cargo.handled(receiveIn(HONGKONG));
216
+
217
+    assertThat(cargo.transportStatus(), is(IN_PORT));
218
+    assertThat(cargo.lastKnownLocation(), is(HONGKONG));
219
+
220
+    // Loaded
221
+    cargo.handled(loadOnto(pacific1).in(HONGKONG));
222
+
223
+    assertThat(cargo.currentVoyage(), is(pacific1));
224
+    assertThat(cargo.lastKnownLocation(), is(HONGKONG));
225
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
226
+    assertThat(cargo.nextExpectedActivity(), is(unloadOff(pacific1).in(LONGBEACH)));
227
+    assertFalse(cargo.isMisdirected());
228
+
229
+    // Unload
230
+    cargo.handled(unloadOff(pacific1).in(LONGBEACH));
231
+    assertFalse(cargo.isMisdirected());
232
+    assertThat(cargo.lastKnownLocation(), is(LONGBEACH));
233
+    assertThat(cargo.transportStatus(), is(IN_PORT));
234
+    assertThat(cargo.nextExpectedActivity(), is(loadOnto(continental1).in(LONGBEACH)));
235
+
236
+    // Load onto wrong voyage
237
+    cargo.handled(loadOnto(pacific2).in(LONGBEACH));
238
+    assertTrue(cargo.isMisdirected());
239
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
240
+    assertNull(cargo.nextExpectedActivity());
241
+    assertNull(cargo.estimatedTimeOfArrival());
242
+
243
+    // Reroute: specify new route
244
+
245
+    assertThat(cargo.earliestReroutingLocation(), is(SEATTLE));
246
+
247
+    // Assign to new route
248
+    List<Itinerary> available = routingService.fetchRoutesForSpecification(
249
+      cargo.routeSpecification().withOrigin(cargo.earliestReroutingLocation())
250
+    );
251
+
252
+    Itinerary newItinerary = selectAppropriateRoute(available);
253
+
254
+    Itinerary mergedItinerary = cargo.itineraryMergedWith(newItinerary);
255
+    cargo.assignToRoute(mergedItinerary);
256
+
257
+    // No longer misdirected
258
+    assertFalse(cargo.isMisdirected());
259
+    assertThat(cargo.routingStatus(), is(ROUTED));
260
+    assertThat(cargo.nextExpectedActivity(), is(unloadOff(pacific2).in(SEATTLE)));
261
+
262
+    // Loaded
263
+    cargo.handled(unloadOff(pacific2).in(SEATTLE));
264
+
265
+    assertFalse(cargo.isMisdirected());
266
+    assertThat(cargo.lastKnownLocation(), is(SEATTLE));
267
+    assertThat(cargo.transportStatus(), is(IN_PORT));
268
+
269
+    // Etc
270
+  }
271
+
272
+
273
+  @Test
190 274
   public void customerRequestsChangeOfDestination() throws Exception {
191 275
     Cargo cargo = setupCargoFromHongkongToStockholm();
192 276