├── .gitignore ├── src ├── test │ └── java │ │ └── com │ │ └── cd │ │ └── acceptance │ │ ├── examples │ │ └── accounting │ │ │ ├── dsl │ │ │ ├── TimeTravel.java │ │ │ ├── drivers │ │ │ │ ├── KYCCheckProtocolDriver.java │ │ │ │ ├── AccountingSystemProtocolDriver.java │ │ │ │ ├── StubExternalKYCCheck.java │ │ │ │ └── DummyAccountingSystemProtocolDriver.java │ │ │ ├── KycDsl.java │ │ │ ├── Dsl.java │ │ │ └── InvoicesDsl.java │ │ │ ├── StubDemonstrationAcceptanceTest.java │ │ │ └── InvoiceSubmissionAcceptanceTest.java │ │ └── dsl │ │ └── ParamsTest.java └── main │ └── java │ └── com │ └── cd │ └── acceptance │ ├── examples │ └── accounting │ │ ├── UserAccessDeniedException.java │ │ ├── UserRole.java │ │ ├── ExternalKYCCheck.java │ │ ├── SubmittedInvoice.java │ │ ├── User.java │ │ ├── Invoice.java │ │ └── DummyAccountingSUT.java │ └── dsl │ └── Params.java └── AcceptanceTestDSLUtils.iml /.gitignore: -------------------------------------------------------------------------------- 1 | /out/ 2 | .idea -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/TimeTravel.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl; 2 | 3 | public @interface TimeTravel { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/UserAccessDeniedException.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | public class UserAccessDeniedException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/UserRole.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | public enum UserRole { 4 | Accountant, AccountingSupervisor, Submitter 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/ExternalKYCCheck.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | public interface ExternalKYCCheck { 4 | boolean verifyAccount(String name); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/SubmittedInvoice.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | public class SubmittedInvoice { 4 | private final User user; 5 | private final Invoice invoice; 6 | 7 | public SubmittedInvoice(User user, Invoice invoice) { 8 | 9 | this.user = user; 10 | this.invoice = invoice; 11 | } 12 | 13 | public User getUser() { 14 | return user; 15 | } 16 | 17 | public Invoice getInvoice() { 18 | return invoice; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/drivers/KYCCheckProtocolDriver.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl.drivers; 2 | 3 | public class KYCCheckProtocolDriver { 4 | private final StubExternalKYCCheck kycCheck; 5 | 6 | public KYCCheckProtocolDriver(StubExternalKYCCheck kycCheck) { 7 | this.kycCheck = kycCheck; 8 | } 9 | 10 | public void approveAccount(String account) { 11 | kycCheck.onVerifyApproveAccount(account); 12 | } 13 | 14 | public void rejectAccount(String account) { 15 | kycCheck.onVerifyRejectAccount(account); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/drivers/AccountingSystemProtocolDriver.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl.drivers; 2 | 3 | import java.util.List; 4 | 5 | public interface AccountingSystemProtocolDriver { 6 | void createAccount(String name, String password, String role); 7 | 8 | void submitInvoice(String userName, String invoiceName, String purchaseOrder, 9 | String invoiceNumber, List items, String total); 10 | 11 | void confirmInvoiceSubmitted(String userName, String invoice); 12 | 13 | void confirmPendingAuthorisations(String userName, String pendingItem); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/User.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | public class User { 4 | private final String name; 5 | private final String password; 6 | private final UserRole role; 7 | 8 | public User(String name, String password, UserRole role) { 9 | 10 | this.name = name; 11 | this.password = password; 12 | this.role = role; 13 | } 14 | 15 | public boolean validate(String pasword) { 16 | return this.password.equals(pasword); 17 | } 18 | 19 | public boolean hasPermission(UserRole role) { 20 | return this.role == role; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/drivers/StubExternalKYCCheck.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl.drivers; 2 | 3 | import com.cd.acceptance.examples.accounting.ExternalKYCCheck; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public class StubExternalKYCCheck implements ExternalKYCCheck { 9 | private final Set validUsers = new HashSet<>(); 10 | 11 | void onVerifyApproveAccount(String account) { 12 | validUsers.add(account); 13 | } 14 | 15 | void onVerifyRejectAccount(String account) { 16 | validUsers.remove(account); 17 | } 18 | 19 | @Override 20 | public boolean verifyAccount(String name) { 21 | return validUsers.contains(name); 22 | } 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/KycDsl.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl; 2 | 3 | import com.cd.acceptance.dsl.Params; 4 | import com.cd.acceptance.examples.accounting.dsl.drivers.KYCCheckProtocolDriver; 5 | 6 | public class KycDsl { 7 | private final Params.DslContext context; 8 | private final KYCCheckProtocolDriver kycCheckDriver; 9 | 10 | public KycDsl(Params.DslContext context, KYCCheckProtocolDriver kycCheckDriver) { 11 | this.context = context; 12 | this.kycCheckDriver = kycCheckDriver; 13 | } 14 | 15 | public void approveAccount(String... args) { 16 | Params params = new Params(context, args); 17 | String account = params.alias("name"); 18 | 19 | kycCheckDriver.approveAccount(account); 20 | } 21 | 22 | public void rejectAccount(String... args) { 23 | Params params = new Params(context, args); 24 | String account = params.alias("name"); 25 | 26 | kycCheckDriver.rejectAccount(account); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | import java.util.List; 4 | 5 | public class Invoice { 6 | private final String invoiceName; 7 | private final String purchaseOrder; 8 | private final String invoiceNumber; 9 | private final List items; 10 | private String total; 11 | 12 | public Invoice(String invoiceName, String purchaseOrder, String invoiceNumber, List items, String total) { 13 | 14 | this.invoiceName = invoiceName; 15 | this.purchaseOrder = purchaseOrder; 16 | this.invoiceNumber = invoiceNumber; 17 | this.items = items; 18 | this.total = total; 19 | } 20 | 21 | public String getInvoiceName() { 22 | return invoiceName; 23 | } 24 | 25 | public String getPurchaseOrder() { 26 | return purchaseOrder; 27 | } 28 | 29 | public String getInvoiceNumber() { 30 | return invoiceNumber; 31 | } 32 | 33 | public List getItems() { 34 | return items; 35 | } 36 | 37 | public String getTotal() { 38 | return total; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/StubDemonstrationAcceptanceTest.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | import com.cd.acceptance.examples.accounting.dsl.Dsl; 4 | import org.junit.jupiter.api.Test; 5 | import org.opentest4j.AssertionFailedError; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | 10 | public class StubDemonstrationAcceptanceTest extends Dsl { 11 | @Test 12 | public void shouldAllowInvoiceSubmissionForApprovedAccounts() { 13 | kyc.approveAccount("name: InvoiceSubmitter1"); 14 | invoices.createAccount("name: InvoiceSubmitter1", "role: Submitter", "kyc: Approved"); 15 | invoices.submitInvoice("name: InvoiceSubmitter1", "invoice: invoice1", "Item: Software License"); 16 | 17 | invoices.confirmInvoiceSubmitted("name: InvoiceSubmitter1", "invoice: invoice1"); 18 | } 19 | 20 | @Test 21 | public void shouldRejectAccountCreationForAccountsRejectedByKyc() throws AssertionFailedError { 22 | AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> 23 | invoices.createAccount("name: InvoiceSubmitter1", "role: Submitter", "kyc: Denied")) ; 24 | assertEquals("Unable to create authorised account for: InvoiceSubmitter1", e.getMessage()); 25 | } 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/Dsl.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl; 2 | 3 | import com.cd.acceptance.dsl.Params; 4 | import com.cd.acceptance.examples.accounting.dsl.drivers.DummyAccountingSystemProtocolDriver; 5 | import com.cd.acceptance.examples.accounting.DummyAccountingSUT; 6 | import com.cd.acceptance.examples.accounting.dsl.drivers.KYCCheckProtocolDriver; 7 | import com.cd.acceptance.examples.accounting.dsl.drivers.StubExternalKYCCheck; 8 | import org.junit.jupiter.api.BeforeEach; 9 | 10 | public class Dsl { 11 | private final Params.DslContext context = new Params.DslContext(); 12 | 13 | protected InvoicesDsl invoices; 14 | protected KycDsl kyc; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | // In reality the Protocol Driver would connect to the real "System Under Test" 19 | // For the purpose of this example, we use a simple class to represent the SUT here. 20 | // A real Protocol Driver would do whatever it takes to translate from DSL to interactions with the SUT 21 | // For example, we may use something like Selenium to interact with a web page, or a REST client to interact with a web service 22 | // and this code would only exist in the Protocol Driver. 23 | 24 | StubExternalKYCCheck kycCheck = new StubExternalKYCCheck(); 25 | DummyAccountingSUT sut = new DummyAccountingSUT(kycCheck); 26 | 27 | kyc = new KycDsl(context, new KYCCheckProtocolDriver(kycCheck)); 28 | invoices = new InvoicesDsl(context, new DummyAccountingSystemProtocolDriver(context, sut,kycCheck), kyc); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/examples/accounting/DummyAccountingSUT.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class DummyAccountingSUT { 9 | private final Map registeredUsers = new HashMap<>(); 10 | private final Map currentUsers = new HashMap<>(); 11 | private final List submittedInvoices = new ArrayList<>(); 12 | private final List pendingReview = new ArrayList<>(); 13 | private final ExternalKYCCheck kycCheck; 14 | 15 | public DummyAccountingSUT(ExternalKYCCheck acctReg) { 16 | this.kycCheck = acctReg; 17 | } 18 | 19 | public void registerUser(String name, String password, UserRole role) { 20 | if (kycCheck.verifyAccount(name)) { 21 | registeredUsers.put(name, new User(name, password, role)); 22 | } 23 | } 24 | 25 | public void loginUser(String name, String pasword) throws UserAccessDeniedException { 26 | User user = registeredUsers.get(name); 27 | if (user == null || !user.validate(pasword)) 28 | throw new UserAccessDeniedException(); 29 | 30 | currentUsers.put(name, user); 31 | } 32 | 33 | public void submitInvoice(String userName, Invoice invoice) throws UserAccessDeniedException { 34 | User user = verifyAccessForUser(userName, UserRole.Submitter); 35 | SubmittedInvoice submittedInvoice = new SubmittedInvoice(user, invoice); 36 | submittedInvoices.add(submittedInvoice); 37 | pendingReview.add(submittedInvoice); 38 | } 39 | 40 | public List listSubmittedInvoices() { 41 | return submittedInvoices; 42 | } 43 | 44 | public List listInvoicesPendingReview(String userName) throws UserAccessDeniedException { 45 | verifyAccessForUser(userName, UserRole.AccountingSupervisor); 46 | 47 | return pendingReview; 48 | } 49 | 50 | private User verifyAccessForUser(String userName, UserRole permissionRequired) throws UserAccessDeniedException { 51 | User user = currentUsers.get(userName); 52 | if (user == null || !user.hasPermission(permissionRequired)) 53 | throw new UserAccessDeniedException(); 54 | return user; 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/InvoicesDsl.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl; 2 | 3 | import com.cd.acceptance.examples.accounting.dsl.drivers.AccountingSystemProtocolDriver; 4 | import com.cd.acceptance.dsl.Params; 5 | 6 | import java.util.List; 7 | 8 | public class InvoicesDsl { 9 | private final Params.DslContext context; 10 | private final AccountingSystemProtocolDriver driver; 11 | private final KycDsl kyc; 12 | 13 | protected InvoicesDsl(Params.DslContext context, AccountingSystemProtocolDriver driver, KycDsl kyc) { 14 | this.context = context; 15 | this.driver = driver; 16 | this.kyc = kyc; 17 | } 18 | 19 | public void createAccount(String... args) { 20 | Params params = new Params(context, args); 21 | 22 | String name = params.alias("name"); 23 | String role = params.optional("role", "Accountant"); 24 | String kycCheck = params.optional("kyc", "Approved"); 25 | String password = params.optional("password", "password123"); 26 | 27 | if ("Approved".equals(kycCheck)) 28 | kyc.approveAccount(args); 29 | else 30 | kyc.rejectAccount(args); 31 | 32 | driver.createAccount(name, password, role); 33 | } 34 | 35 | public void submitInvoice(String... args) { 36 | Params params = new Params(context, args); 37 | 38 | String userName = params.alias("name"); 39 | String invoice = params.alias("invoice", "anInvoice"); 40 | String purchaseOrder = params.optional("po", "po1"); 41 | String invoiceNumber = params.optionalSequence("InvoiceNo", 1); 42 | String total = params.optional("total", "0"); 43 | List items = params.optionalList("item", new String[] {"item1"}); 44 | 45 | driver.submitInvoice(userName, invoice, purchaseOrder, invoiceNumber, items, total); 46 | } 47 | 48 | public void confirmInvoiceSubmitted(String... args) { 49 | Params params = new Params(context, args); 50 | 51 | String userName = params.alias("name"); 52 | String invoice = params.optional("invoice", "anInvoice"); 53 | 54 | driver.confirmInvoiceSubmitted(userName, this.context.alias(invoice)); 55 | } 56 | 57 | public void confirmPendingAuthorisations(String... args) { 58 | Params params = new Params(context, args); 59 | 60 | String userName = params.alias("name"); 61 | String pendingItem = params.optional("pendingItem", "someItem"); 62 | 63 | driver.confirmPendingAuthorisations(userName, context.alias(pendingItem)); 64 | } 65 | 66 | public void confirmInvoiceRejected(String... args) { 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /AcceptanceTestDSLUtils.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/InvoiceSubmissionAcceptanceTest.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting; 2 | 3 | import com.cd.acceptance.examples.accounting.dsl.Dsl; 4 | import org.junit.jupiter.api.Test; 5 | import org.opentest4j.AssertionFailedError; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | 10 | public class InvoiceSubmissionAcceptanceTest extends Dsl { 11 | @Test 12 | public void shouldAcknowledgeInvoiceSubmission() { 13 | invoices.createAccount("name: InvoiceSubmitter1", "role: Submitter"); 14 | invoices.submitInvoice( 15 | "name: InvoiceSubmitter1", "invoice: invoice1", "Item: Software License"); 16 | invoices.confirmInvoiceSubmitted("name: InvoiceSubmitter1", "invoice: invoice1"); 17 | } 18 | 19 | @Test 20 | public void should() { 21 | 22 | invoices.createAccount("name: Account1"); 23 | 24 | invoices.createAccount("name: Account2", "role: Submitter", 25 | "kyc: Rejected", "password: fred1234"); 26 | } 27 | 28 | 29 | 30 | @Test 31 | public void shouldSendSubmittedInvoiceToSupervisor() { 32 | invoices.createAccount("name: InvoiceSubmitter1", "role: Submitter"); 33 | invoices.createAccount("name: Supervisor1", "role: AccountingSupervisor"); 34 | invoices.submitInvoice( 35 | "name: InvoiceSubmitter1", "invoice: invoice1", "Item: Software License"); 36 | invoices.confirmPendingAuthorisations("name: Supervisor1", "pendingItem: invoice1"); 37 | } 38 | 39 | @Test 40 | public void shouldAllowInvoiceSubmissionForApprovedSubmitterAccounts() { 41 | invoices.createAccount("name: InvoiceSubmitter1", "role: Submitter"); 42 | invoices.submitInvoice("name: InvoiceSubmitter1", "invoice: invoice1", "Item: Software License"); 43 | invoices.confirmInvoiceSubmitted("name: InvoiceSubmitter1", "invoice: invoice1"); 44 | } 45 | 46 | @Test 47 | public void shouldRejectInvoiceSubmissionForNoneSubmitterAccounts() throws AssertionFailedError { 48 | invoices.createAccount("name: NoneSubmitter1"); 49 | 50 | AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> 51 | invoices.submitInvoice("name: NoneSubmitter1", "invoice: invoice1", "Item: Software License")); 52 | 53 | assertEquals("User 'NoneSubmitter1' does not have permission to submit invoices", e.getMessage()); 54 | } 55 | 56 | } 57 | 58 | //Given an authorised account “invoice Submitter 1” 59 | //Given an invoice for a software license, invoice1 60 | //When “invoice Submitter 1” submits invoice1 61 | //Then invoice1 is acknowledged to “invoice Submitter 1”. 62 | 63 | //Given an authorised account “invoice Submitter 1” 64 | //Given an authorised account “accounting Supervisor 1” 65 | //Given an invoice for a software license, invoice1 66 | //When “invoice Submitter 1” submits invoice1 67 | //Then invoice1 is sent to “accounting supervisor1” as "pending Authorisation" 68 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/examples/accounting/dsl/drivers/DummyAccountingSystemProtocolDriver.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.examples.accounting.dsl.drivers; 2 | 3 | import com.cd.acceptance.dsl.Params; 4 | import com.cd.acceptance.examples.accounting.*; 5 | 6 | import java.util.List; 7 | 8 | import static org.junit.jupiter.api.Assertions.fail; 9 | 10 | // Pretend System Under Test 11 | public class DummyAccountingSystemProtocolDriver implements AccountingSystemProtocolDriver { 12 | private final Params.DslContext context; 13 | private final DummyAccountingSUT accountingSystem; 14 | private final StubExternalKYCCheck kycCheckDriver; 15 | 16 | public DummyAccountingSystemProtocolDriver(Params.DslContext context, DummyAccountingSUT accountingSystem, StubExternalKYCCheck kycCheckDriver) { 17 | this.context = context; 18 | this.accountingSystem = accountingSystem; 19 | this.kycCheckDriver = kycCheckDriver; 20 | } 21 | 22 | @Override 23 | public void createAccount(String name, String password, String role) { 24 | try { 25 | accountingSystem.registerUser(name, password, UserRole.valueOf(role)); 26 | accountingSystem.loginUser(name, password); 27 | } catch (UserAccessDeniedException e) 28 | { 29 | Params params = new Params(context); 30 | fail("Unable to create authorised account for: " + params.decodeAlias(name)); 31 | } 32 | } 33 | 34 | @Override 35 | public void submitInvoice(String userName, String invoiceName, String purchaseOrder, String invoiceNumber, List items, String total) { 36 | try 37 | { 38 | Invoice invoice = new Invoice(invoiceName, purchaseOrder, invoiceNumber, items, total); 39 | accountingSystem.submitInvoice(userName, invoice); 40 | } catch (UserAccessDeniedException e) { 41 | Params params = new Params(context); 42 | fail("User '" + params.decodeAlias(userName) + "' does not have permission to submit invoices"); 43 | } 44 | } 45 | 46 | @Override 47 | public void confirmInvoiceSubmitted(String userName, String invoice) { 48 | List invoices = accountingSystem.listSubmittedInvoices(); 49 | for (SubmittedInvoice item : invoices) { 50 | if (item.getUser().getName().equals(userName) && item.getInvoice().getInvoiceName().equals(invoice)) { 51 | return; 52 | } 53 | } 54 | fail("Invoice '" + invoice + "' does not exist!"); 55 | } 56 | 57 | @Override 58 | public void confirmPendingAuthorisations(String userName, String pendingItem) { 59 | try { 60 | List invoices = accountingSystem.listInvoicesPendingReview(userName); 61 | for (SubmittedInvoice item : invoices) { 62 | if (item.getInvoice().getInvoiceName().equals(pendingItem)) { 63 | return; 64 | } 65 | fail("'" + pendingItem + "' Is not pending review!"); 66 | } 67 | } catch (UserAccessDeniedException e) 68 | { 69 | fail("Access Denied for User '" + userName + "'"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/cd/acceptance/dsl/Params.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.dsl; 2 | 3 | import java.util.*; 4 | import static org.junit.jupiter.api.Assertions.fail; 5 | 6 | /** 7 | * Copyright (c) Continuous Delivery Ltd. 2024 8 | */ 9 | public class Params 10 | { 11 | private final DslContext context; 12 | private final List args; 13 | 14 | public Params(DslContext context, String[] args) 15 | { 16 | this.context = context; 17 | this.args = new ArrayList<>(Arrays.asList(args)); 18 | } 19 | 20 | public Params(DslContext context) { 21 | this(context, new String[0]); 22 | } 23 | 24 | public String optional(String name, String defaultValue) 25 | { 26 | String arg = getParamValue(name); 27 | if (arg != null) return arg; 28 | return defaultValue; 29 | } 30 | 31 | public String alias(String name) 32 | { 33 | String value = getParamValue(name); 34 | if (value == null) { 35 | fail("No '" + name + "' supplied for alias"); 36 | } 37 | return context.alias(value); 38 | } 39 | 40 | public String alias(String name, String defaultValue) 41 | { 42 | String value = getParamValue(name); 43 | if (value == null) { 44 | args.add(name + ": " + defaultValue); 45 | } 46 | return alias(name); 47 | } 48 | 49 | public String decodeAlias(String alias) { 50 | return context.decodeAlias(alias); 51 | } 52 | 53 | public String optionalSequence(String name, int start) { 54 | return optional(name, context.sequenceNumberForName(name, start)); 55 | } 56 | 57 | private String getParamValue(String name) { 58 | for (String arg : args) 59 | { 60 | int index = arg.indexOf(name + ": "); 61 | if (index != -1) 62 | { 63 | return arg.substring(index + name.length() + 2); 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | public List optionalList(String name, String[] items) { 70 | return null; 71 | } 72 | public static class DslContext { 73 | 74 | private static final Map globalSequenceNumbers = new HashMap<>(); 75 | private final Map sequenceNumbers = new HashMap<>(); 76 | private final Map aliases = new HashMap<>(); 77 | 78 | public String sequenceNumberForName(String name, int start) { 79 | return seqForName(name, start, sequenceNumbers); 80 | } 81 | 82 | public String alias(String name) { 83 | if (!aliases.containsKey(name)) { 84 | String sequenceNo = seqForName(name, 1, globalSequenceNumbers); 85 | aliases.put(name, name + sequenceNo); 86 | } 87 | return aliases.get(name); 88 | } 89 | 90 | private String seqForName(String name, int start, Map sequenceNumbers) { 91 | int retVal = start; 92 | if (sequenceNumbers.containsKey(name)) { 93 | retVal = sequenceNumbers.get(name); 94 | } 95 | sequenceNumbers.put(name, retVal + 1); 96 | 97 | return String.valueOf(retVal); 98 | } 99 | 100 | public String decodeAlias(String name) { 101 | for (String key : aliases.keySet()) { 102 | if (aliases.get(key).equals(name)) { 103 | return key; 104 | } 105 | } 106 | return ""; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/com/cd/acceptance/dsl/ParamsTest.java: -------------------------------------------------------------------------------- 1 | package com.cd.acceptance.dsl; 2 | 3 | 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.opentest4j.AssertionFailedError; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | public class ParamsTest { 11 | public static final String[] EMPTY_ARGS = new String[0]; 12 | private Params.DslContext context; 13 | 14 | @BeforeEach 15 | public void setUp(){ 16 | context = new Params.DslContext(); 17 | } 18 | 19 | @Test 20 | public void shouldReturnOptionalValueOfParam() { 21 | Params params = new Params(context, EMPTY_ARGS); 22 | 23 | assertEquals("3", params.optional("Three", "3")); 24 | } 25 | 26 | @Test 27 | public void shouldReturnDefinedValueOverridingDefault() { 28 | Params params = new Params(context, new String[]{"One: 1"}); 29 | 30 | assertEquals("1", params.optional("One", "3")); 31 | } 32 | 33 | @Test 34 | public void shouldReturnStartValueOfOptionalSequence() { 35 | Params params = new Params(context); 36 | 37 | assertEquals("3", params.optionalSequence("Param1", 3)); 38 | } 39 | 40 | @Test 41 | public void shouldReturnNextValueOfOptionalSequence() { 42 | Params params = new Params(context); 43 | 44 | params.optionalSequence("SomeParam", 5); 45 | assertEquals("6", params.optionalSequence("SomeParam", 5)); 46 | } 47 | 48 | @Test 49 | public void shouldReturnValueOverridingOptionalSequenceValue() { 50 | Params params = new Params(context, new String[]{"SomeParam: 1"}); 51 | 52 | assertEquals("1", params.optionalSequence("SomeParam", 3)); 53 | } 54 | 55 | @Test 56 | public void shouldAliasNameWithDifferentValue() { 57 | Params params = new Params(context, new String[]{"name: nameTest"}); 58 | String aliasedName = params.alias("name"); 59 | 60 | assertNotEquals("name", aliasedName); 61 | } 62 | 63 | @Test 64 | public void shouldAliasNamesWithUniqueValue() { 65 | Params params = new Params(context, new String[]{"name1: nameTest", "name2: nameTest2"}); 66 | String aliasedName1 = params.alias("name1"); 67 | String aliasedName2 = params.alias("name2"); 68 | 69 | assertNotEquals(aliasedName1, aliasedName2); 70 | } 71 | 72 | @Test 73 | public void shouldReturnOriginalValueGivenAlias() { 74 | Params params = new Params(context); 75 | 76 | String alias = params.alias("name", "someName"); 77 | 78 | assertEquals("someName", params.decodeAlias(alias)); 79 | } 80 | 81 | @Test 82 | public void shouldReturnEmptyStringWhenDecodingAndAliasNotFound() { 83 | Params params = new Params(context); 84 | 85 | assertEquals("", params.decodeAlias("UnknonwnAlias")); 86 | } 87 | @Test() 88 | public void shouldFailAliasIfValueNotPresent() { 89 | Params params = new Params(context, new String[]{"name: nameTest"}); 90 | assertThrows(AssertionFailedError.class, () -> params.alias("name2")); 91 | } 92 | 93 | @Test 94 | public void shouldSupplyConsistentAliasWithinContext() { 95 | Params params = new Params(context, new String[]{"name: nameTest"}); 96 | String aliasedName = params.alias("name"); 97 | 98 | assertEquals(aliasedName, params.alias("name")); 99 | } 100 | 101 | @Test 102 | public void shouldSupplyDifferentAliasForSameNameAcrossContexts() { 103 | Params params = new Params(context, new String[]{"name: nameTest"}); 104 | Params.DslContext otherContext = new Params.DslContext(); 105 | Params otherParams = new Params(otherContext, new String[]{"name: nameTest"}); 106 | 107 | assertNotEquals(params.alias("name"), otherParams.alias("name")); 108 | } 109 | 110 | @Test 111 | public void shouldUseSuppliedNameAsRootOfAlias() { 112 | Params params = new Params(context, new String[]{"name: nameTest"}); 113 | assertTrue(params.alias("name").startsWith("nameTest")); 114 | } 115 | 116 | @Test 117 | public void shouldUseDefaultValueAsRootOfAlias() { 118 | Params params = new Params(context, new String[0]); 119 | assertTrue(params.alias("name", "defaultValue").startsWith("defaultValue")); 120 | } 121 | } 122 | --------------------------------------------------------------------------------