From cf637713e9123ca4fa2f90062f12a94dd2253c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=A2=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D1=85=D0=BE=D0=B2?= Date: Sat, 3 Aug 2024 08:32:20 +0400 Subject: [PATCH] test(server): testing the web server config page Refs: #52 --- .../ru/ewc/checklogic/FileStateFactory.java | 119 ++++++++++++++++++ .../ru/ewc/checklogic/FullServerContext.java | 5 +- .../java/ru/ewc/checklogic/LogicChecker.java | 2 +- .../ru/ewc/checklogic/MockStateFactory.java | 59 +++++++++ .../ewc/checklogic/ServerContextFactory.java | 23 +++- .../java/ru/ewc/checklogic/StateFactory.java | 91 +++----------- .../ru/ewc/checklogic/server/ContextPage.java | 6 +- .../ru/ewc/checklogic/server/WebPages.java | 2 +- .../checklogic/server/ContextPageTest.java | 57 +++++++++ .../ewc/checklogic/server/package-info.java | 27 ++++ 10 files changed, 305 insertions(+), 86 deletions(-) create mode 100644 src/main/java/ru/ewc/checklogic/FileStateFactory.java create mode 100644 src/main/java/ru/ewc/checklogic/MockStateFactory.java create mode 100644 src/test/java/ru/ewc/checklogic/server/ContextPageTest.java create mode 100644 src/test/java/ru/ewc/checklogic/server/package-info.java diff --git a/src/main/java/ru/ewc/checklogic/FileStateFactory.java b/src/main/java/ru/ewc/checklogic/FileStateFactory.java new file mode 100644 index 0000000..58c5a4d --- /dev/null +++ b/src/main/java/ru/ewc/checklogic/FileStateFactory.java @@ -0,0 +1,119 @@ +/* + * MIT License + * + * Copyright (c) 2024 Decision-Driven Development + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package ru.ewc.checklogic; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.SneakyThrows; +import org.yaml.snakeyaml.Yaml; +import ru.ewc.decisions.api.Locator; +import ru.ewc.state.State; + +/** + * I am a factory for creating the state of the system. + * + * @since 0.3.2 + */ +public final class FileStateFactory extends StateFactory { + + /** + * Optional source of locators to be added to the initial (clean) state. + */ + private InputStream src; + + public FileStateFactory(final String root) { + super(root); + } + + @Override + @SneakyThrows + public State initialState() { + State state; + try (InputStream file = Files.newInputStream(Path.of(this.getRoot(), "application.yaml"))) { + state = FileStateFactory.stateFromAppConfig(file); + state.locators().putAll(this.locatorsFromFile()); + } catch (final NoSuchFileException exception) { + state = new NullState(Map.of()); + } + return state; + } + + @Override + public StateFactory with(final InputStream file) { + this.src = file; + return this; + } + + @Override + @SneakyThrows + public void initialize() { + final File config = Path.of(this.getRoot(), "application.yaml").toFile(); + if (!config.exists() && config.createNewFile()) { + try (OutputStream out = Files.newOutputStream(config.toPath())) { + out.write("locators:\n - request\n".getBytes(StandardCharsets.UTF_8)); + } + } + } + + @SuppressWarnings("unchecked") + private Map locatorsFromFile() { + final Map locators = new HashMap<>(); + if (this.src != null) { + final Map> raw = + (Map>) new Yaml().loadAll(this.src).iterator().next(); + if (raw == null) { + throw new IllegalStateException( + "There is no Arrange section in the test file, you should add one" + ); + } + raw.forEach((name, data) -> locators.put(name, new InMemoryStorage(data))); + } + return locators; + } + + @SneakyThrows + @SuppressWarnings("unchecked") + private static State stateFromAppConfig(final InputStream file) { + final Map config = new Yaml().load(file); + final Stream names = ((List) config.get("locators")).stream(); + return new State( + names.collect( + Collectors.toMap( + name -> name, + name -> new InMemoryStorage(new HashMap<>()) + ) + ) + ); + } +} diff --git a/src/main/java/ru/ewc/checklogic/FullServerContext.java b/src/main/java/ru/ewc/checklogic/FullServerContext.java index 0650db5..42a5eae 100644 --- a/src/main/java/ru/ewc/checklogic/FullServerContext.java +++ b/src/main/java/ru/ewc/checklogic/FullServerContext.java @@ -75,7 +75,6 @@ public final class FullServerContext { */ private final StateFactory states; - // @todo #52 Create the testable instance of FullServerContext FullServerContext( final StateFactory initial, final URI tables, @@ -90,6 +89,10 @@ public final class FullServerContext { this.context = new ComputationContext(this.state, tables, commands); } + public static FullServerContext testable() { + return ServerContextFactory.testable().initialState(); + } + public void perform(final String command) { this.perform(command, Map.of()); } diff --git a/src/main/java/ru/ewc/checklogic/LogicChecker.java b/src/main/java/ru/ewc/checklogic/LogicChecker.java index b267c3f..8006d55 100644 --- a/src/main/java/ru/ewc/checklogic/LogicChecker.java +++ b/src/main/java/ru/ewc/checklogic/LogicChecker.java @@ -49,7 +49,7 @@ public static void main(final String[] args) { throw new IllegalArgumentException("Please provide the path to the resources"); } final String root = args[0]; - final FullServerContext context = new ServerContextFactory(root).initialState(); + final FullServerContext context = ServerContextFactory.create(root).initialState(); final FullSystem minum = FullSystem.initialize(); final WebFramework web = minum.getWebFramework(); registerEndpoints(web, new CommandPage(context)); diff --git a/src/main/java/ru/ewc/checklogic/MockStateFactory.java b/src/main/java/ru/ewc/checklogic/MockStateFactory.java new file mode 100644 index 0000000..0eb3b5c --- /dev/null +++ b/src/main/java/ru/ewc/checklogic/MockStateFactory.java @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2024 Decision-Driven Development + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package ru.ewc.checklogic; + +import java.io.InputStream; +import java.util.Map; +import ru.ewc.state.State; + +/** + * I am a mock state factory for testing purposes. + * + * @since 0.3.2 + */ +public final class MockStateFactory extends StateFactory { + public MockStateFactory(final String root) { + super(root); + } + + @Override + public State initialState() { + return new State( + Map.of( + "locator", new InMemoryStorage(Map.of("fragment", "value")), + "request", new InMemoryStorage(Map.of()) + ) + ); + } + + @Override + public StateFactory with(final InputStream file) { + return this; + } + + @Override + public void initialize() { + // do nothing + } +} diff --git a/src/main/java/ru/ewc/checklogic/ServerContextFactory.java b/src/main/java/ru/ewc/checklogic/ServerContextFactory.java index 9c8fccf..6002743 100644 --- a/src/main/java/ru/ewc/checklogic/ServerContextFactory.java +++ b/src/main/java/ru/ewc/checklogic/ServerContextFactory.java @@ -31,14 +31,29 @@ * * @since 0.3.2 */ -public class ServerContextFactory { +@SuppressWarnings("PMD.ProhibitPublicStaticMethods") +public final class ServerContextFactory { /** * The root path for the external business logic resources. */ private final String root; - public ServerContextFactory(final String root) { + /** + * The factory for creating the state of the system. + */ + private final StateFactory factory; + + private ServerContextFactory(final String root, final StateFactory factory) { this.root = root; + this.factory = factory; + } + + public static ServerContextFactory testable() { + return new ServerContextFactory("root folder", new MockStateFactory("root folder")); + } + + public static ServerContextFactory create(final String root) { + return new ServerContextFactory(root, new FileStateFactory(root)); } /** @@ -48,7 +63,7 @@ public ServerContextFactory(final String root) { */ public FullServerContext initialState() { final FullServerContext result = new FullServerContext( - new StateFactory(this.root), + this.factory, Path.of(this.root, "tables").toUri(), Path.of(this.root, "commands").toUri(), new WebServerContext() @@ -67,7 +82,7 @@ public FullServerContext initialState() { */ public FullServerContext fromStateFile(final InputStream file) { return new FullServerContext( - new StateFactory(this.root).with(file), + this.factory.with(file), Path.of(this.root, "tables").toUri(), Path.of(this.root, "commands").toUri(), new WebServerContext() diff --git a/src/main/java/ru/ewc/checklogic/StateFactory.java b/src/main/java/ru/ewc/checklogic/StateFactory.java index 344a380..00790b6 100644 --- a/src/main/java/ru/ewc/checklogic/StateFactory.java +++ b/src/main/java/ru/ewc/checklogic/StateFactory.java @@ -23,101 +23,38 @@ */ package ru.ewc.checklogic; -import java.io.File; import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Getter; -import lombok.SneakyThrows; -import org.yaml.snakeyaml.Yaml; -import ru.ewc.decisions.api.Locator; import ru.ewc.state.State; /** - * I am a factory for creating the state of the system. + * I am a factory for creating state objects. My subclasses are responsible for creating the initial + * state and loading the state from a file. They can do that for real or mock the result for testing + * purposes. * * @since 0.3.2 */ -public final class StateFactory { +public abstract class StateFactory { /** * The root path for the external business logic resources. */ - @Getter private final String root; - /** - * Optional source of locators to be added to the initial (clean) state. - */ - private InputStream src; - - // @todo #54 Create testable instance of StateFactory public StateFactory(final String root) { this.root = root; } - @SneakyThrows - public State initialState() { - State state; - try (InputStream file = Files.newInputStream(Path.of(this.root, "application.yaml"))) { - state = StateFactory.stateFromAppConfig(file); - state.locators().putAll(this.locatorsFromFile()); - } catch (final NoSuchFileException exception) { - state = new NullState(Map.of()); - } - return state; - } - - public StateFactory with(final InputStream file) { - this.src = file; - return this; + /** + * Returns the path to the root folder of the external business logic resources. + * + * @return Path to the root folder as a string. + */ + public String getRoot() { + return this.root; } - @SneakyThrows - public void initialize() { - final File config = Path.of(this.root, "application.yaml").toFile(); - if (!config.exists() && config.createNewFile()) { - try (OutputStream out = Files.newOutputStream(config.toPath())) { - out.write("locators:\n - request\n".getBytes(StandardCharsets.UTF_8)); - } - } - } + public abstract State initialState(); - @SuppressWarnings("unchecked") - private Map locatorsFromFile() { - final Map locators = new HashMap<>(); - if (this.src != null) { - final Map> raw = - (Map>) new Yaml().loadAll(this.src).iterator().next(); - if (raw == null) { - throw new IllegalStateException( - "There is no Arrange section in the test file, you should add one" - ); - } - raw.forEach((name, data) -> locators.put(name, new InMemoryStorage(data))); - } - return locators; - } + public abstract StateFactory with(InputStream file); - @SneakyThrows - @SuppressWarnings("unchecked") - private static State stateFromAppConfig(final InputStream file) { - final Map config = new Yaml().load(file); - final Stream names = ((List) config.get("locators")).stream(); - return new State( - names.collect( - Collectors.toMap( - name -> name, - name -> new InMemoryStorage(new HashMap<>()) - ) - ) - ); - } + public abstract void initialize(); } diff --git a/src/main/java/ru/ewc/checklogic/server/ContextPage.java b/src/main/java/ru/ewc/checklogic/server/ContextPage.java index 0a4b80d..f97b7d7 100644 --- a/src/main/java/ru/ewc/checklogic/server/ContextPage.java +++ b/src/main/java/ru/ewc/checklogic/server/ContextPage.java @@ -52,7 +52,7 @@ public void register(final WebFramework web) { web.registerPath(POST, "context", this::contextPage); } - private Response contextPage(final Request request) { + Response contextPage(final Request request) { this.context.cache("command", request.body().asString("availOutcome")); this.context.cache("request", request.body().asString("reqLocator")); this.updateContext(request.body().asString("reqValues")); @@ -66,6 +66,8 @@ private String availabilityField() { private void updateContext(final String values) { final String decoded = URLDecoder.decode(values, StandardCharsets.UTF_8); - this.context.update(Arrays.stream(decoded.split("\n")).collect(Collectors.toList())); + if (!decoded.isBlank()) { + this.context.update(Arrays.stream(decoded.split("\n")).collect(Collectors.toList())); + } } } diff --git a/src/main/java/ru/ewc/checklogic/server/WebPages.java b/src/main/java/ru/ewc/checklogic/server/WebPages.java index df4a81f..5557184 100644 --- a/src/main/java/ru/ewc/checklogic/server/WebPages.java +++ b/src/main/java/ru/ewc/checklogic/server/WebPages.java @@ -122,7 +122,7 @@ private TestResult performTest(final TestData test) { TestResult result; final FullServerContext target; try { - target = new ServerContextFactory(this.root) + target = ServerContextFactory.create(this.root) .fromStateFile(Files.newInputStream(new File(test.file()).toPath())); try { if (!test.command().isEmpty()) { diff --git a/src/test/java/ru/ewc/checklogic/server/ContextPageTest.java b/src/test/java/ru/ewc/checklogic/server/ContextPageTest.java new file mode 100644 index 0000000..8e8180d --- /dev/null +++ b/src/test/java/ru/ewc/checklogic/server/ContextPageTest.java @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2024 Decision-Driven Development + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package ru.ewc.checklogic.server; + +import com.renomad.minum.web.Body; +import com.renomad.minum.web.Headers; +import com.renomad.minum.web.Request; +import com.renomad.minum.web.RequestLine; +import com.renomad.minum.web.Response; +import java.nio.charset.StandardCharsets; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import ru.ewc.checklogic.FullServerContext; + +/** + * I test the {@link ContextPage} class. + * + * @since 0.3.2 + */ +final class ContextPageTest { + @Test + void shouldCreateMockServer() { + final ContextPage target = new ContextPage(FullServerContext.testable()); + final Response response = target.contextPage(ContextPageTest.emptyRequest()); + MatcherAssert.assertThat( + "Mock server should not have any commands available", + new String(response.getBody(), StandardCharsets.UTF_8), + Matchers.is("") + ); + } + + private static Request emptyRequest() { + return new Request(Headers.EMPTY, RequestLine.empty(), Body.EMPTY, null); + } +} diff --git a/src/test/java/ru/ewc/checklogic/server/package-info.java b/src/test/java/ru/ewc/checklogic/server/package-info.java new file mode 100644 index 0000000..f77fd30 --- /dev/null +++ b/src/test/java/ru/ewc/checklogic/server/package-info.java @@ -0,0 +1,27 @@ +/* + * MIT License + * + * Copyright (c) 2024 Decision-Driven Development + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/** + * Package for server-side classes' tests. + */ +package ru.ewc.checklogic.server;