1 package com.guinetik.hexafun.testing;
2
3 import com.guinetik.hexafun.HexaApp;
4 import com.guinetik.hexafun.fun.Result;
5 import java.util.function.Consumer;
6 import java.util.function.Function;
7 import java.util.function.Predicate;
8
9
10
11
12
13
14 public class UseCaseTest<I, O> {
15 private final HexaApp app;
16 private final String useCaseName;
17 private I input;
18 private O output;
19 private Exception exception;
20 private boolean executed = false;
21
22 UseCaseTest(HexaApp app, String useCaseName) {
23 this.app = app;
24 this.useCaseName = useCaseName;
25 }
26
27
28
29
30
31
32 public UseCaseTest<I, O> with(I input) {
33 this.input = input;
34 return this;
35 }
36
37
38
39
40
41
42 public UseCaseTest<I, O> expectOk(Consumer<O> verifier) {
43 executeIfNeeded();
44 if (exception != null) {
45 throw new AssertionError("Expected successful result but got exception: " + exception.getMessage(), exception);
46 }
47
48 if (output instanceof Result) {
49 Result<?> result = (Result<?>) output;
50 if (result.isFailure()) {
51 throw new AssertionError("Expected successful result but got failure: " + result.error());
52 }
53 @SuppressWarnings("unchecked")
54 O unwrapped = (O) result.get();
55 verifier.accept(unwrapped);
56 } else {
57 verifier.accept(output);
58 }
59
60 return this;
61 }
62
63
64
65
66
67
68 public UseCaseTest<I, O> expectFailure(Consumer<String> errorVerifier) {
69 executeIfNeeded();
70 if (exception != null) {
71 errorVerifier.accept(exception.getMessage());
72 return this;
73 }
74
75 if (output instanceof Result) {
76 Result<?> result = (Result<?>) output;
77 if (result.isSuccess()) {
78 throw new AssertionError("Expected failure but got successful result: " + result.get());
79 }
80 errorVerifier.accept(result.error());
81 } else {
82 throw new AssertionError("Expected Result type but got: " + output.getClass().getName());
83 }
84
85 return this;
86 }
87
88
89
90
91
92
93
94 public UseCaseTest<I, O> expect(Predicate<O> predicate, String description) {
95 executeIfNeeded();
96 if (exception != null) {
97 throw new AssertionError("Expected result but got exception: " + exception.getMessage(), exception);
98 }
99
100 if (!predicate.test(output)) {
101 throw new AssertionError("Expected " + description + " but was not satisfied by " + output);
102 }
103
104 return this;
105 }
106
107
108
109
110
111
112 public UseCaseTest<I, O> expectException(Class<? extends Exception> exceptionClass) {
113 executeIfNeeded();
114 if (exception == null) {
115 throw new AssertionError("Expected exception of type " + exceptionClass.getName() + " but no exception was thrown");
116 }
117
118 if (!exceptionClass.isInstance(exception)) {
119 throw new AssertionError("Expected exception of type " + exceptionClass.getName()
120 + " but got " + exception.getClass().getName(), exception);
121 }
122
123 return this;
124 }
125
126
127
128
129
130
131
132 public <T> UseCaseTest<I, T> map(Function<O, T> mapper) {
133 executeIfNeeded();
134 if (exception != null) {
135 throw new AssertionError("Cannot map result: an exception occurred: " + exception.getMessage(), exception);
136 }
137
138 UseCaseTest<I, T> mappedTest = new UseCaseTest<>(app, useCaseName);
139 mappedTest.input = this.input;
140 mappedTest.executed = true;
141
142 try {
143 mappedTest.output = mapper.apply(output);
144 } catch (Exception e) {
145 mappedTest.exception = e;
146 }
147
148 return mappedTest;
149 }
150
151
152
153
154 private void executeIfNeeded() {
155 if (executed) {
156 return;
157 }
158
159 try {
160 output = app.invokeByName(useCaseName, input);
161 } catch (Exception e) {
162 exception = e;
163 }
164
165 executed = true;
166 }
167 }