diff options
-rw-r--r-- | homeeddyprojectscardbasetestsave.cb | 1 | ||||
-rw-r--r-- | res/help_en (renamed from res/help) | 0 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/cli/CardbaseCLI.java | 52 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/utils/MTGUniverse.java | 7 | ||||
-rw-r--r-- | test/eu/equalparts/cardbase/CardbaseSortTest.java | 2 | ||||
-rw-r--r-- | test/eu/equalparts/cardbase/CardbaseTest.java | 62 | ||||
-rw-r--r-- | test/eu/equalparts/cardbase/cli/CardbaseCLITest.java | 260 | ||||
-rw-r--r-- | test/eu/equalparts/cardbase/testutils/TestFile.java | 23 | ||||
-rw-r--r-- | test/eu/equalparts/cardbase/testutils/TestUtils.java | 24 | ||||
-rw-r--r-- | test/notjson.txt | 1 | ||||
-rw-r--r-- | todo | 1 |
11 files changed, 314 insertions, 119 deletions
diff --git a/homeeddyprojectscardbasetestsave.cb b/homeeddyprojectscardbasetestsave.cb deleted file mode 100644 index 17fb803..0000000 --- a/homeeddyprojectscardbasetestsave.cb +++ /dev/null @@ -1 +0,0 @@ -{"1621415":{"name":"Shivan Dragon","layout":"normal","manaCost":"{4}{R}{R}","cmc":6,"type":"Creature — Dragon","rarity":"Rare","text":"Flying (This creature can't be blocked except by creatures with flying or reach.)\n{R}: Shivan Dragon gets +1/+0 until end of turn.","flavor":"The undisputed master of the mountains of Shiv.","artist":"Donato Giancola","number":"281","power":"5","toughness":"5","loyalty":null,"multiverseid":383172,"imageName":"shivan dragon","watermark":null,"setCode":"M15","imageCode":"m15","count":1}}
\ No newline at end of file diff --git a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java index e621bb5..0b2c869 100644 --- a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java +++ b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java @@ -38,7 +38,7 @@ public final class CardbaseCLI { /** * Location of the help file. */ - static final String HELP_FILE_PATH = "/help"; + static final String HELP_FILE_PATH = "/help_en"; /** * Program version. */ @@ -79,7 +79,26 @@ public final class CardbaseCLI { * @param args the first argument is the cardbase file. Further arguments are ignored. */ public static void main(String... args) { - new CardbaseCLI(args).startInterface(); + try { + new CardbaseCLI(args).startInterface(); + } catch (JsonParseException e) { + System.out.println("Error: poorly formatted cardbase, check the syntax and try again."); + // although the problem could also be with the upstream CardSetList json. + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } catch (JsonMappingException e) { + System.out.println("Error: unexpected fields found in cardbase, it may be from an old version?"); + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } catch (IOException e) { + System.out.println("Error: something went wrong reading cardbase file, abort..."); + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } catch (IllegalArgumentException e) { + System.out.println("Error: the provided file is invalid."); + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } } /** @@ -88,37 +107,24 @@ public final class CardbaseCLI { * on the constructed object. * * @param args a list of arguments. Only the first argument is used, as a cardbase JSON. + * @throws IOException if something goes wrong while reading the provided file. + * @throws JsonMappingException if the provided json did not correspond to the expected format. + * @throws JsonParseException if the provided file did not contain valid json. */ - CardbaseCLI(String... args) { + CardbaseCLI(String... args) throws JsonParseException, JsonMappingException, IOException { System.out.println("Welcome to Cardbase CLI!"); // set debug flag if we are debugging if (Cardbase.DEBUG) System.out.println("Debug mode is on."); // make the Cardbase - if (args != null && args.length > 0) { + if (args != null && args.length > 0 && !args[0].isEmpty()) { cardbaseFile = new File(args[0]); if (cardbaseFile.exists() && cardbaseFile.isFile() && cardbaseFile.canRead()) { System.out.println("Loading cardbase from \"" + args[0] + "\"."); - try { - cardbase = new Cardbase(cardbaseFile); - } catch (JsonParseException e) { - System.out.println("Error: poorly formatted cardbase, check the syntax and try again."); - // although the problem could also be with the upstream CardSetList json. - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } catch (JsonMappingException e) { - System.out.println("Error: unexpected fields found in cardbase, it may be from an old version?"); - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } catch (IOException e) { - System.out.println("Error: something went wrong reading cardbase file, abort..."); - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } + cardbase = new Cardbase(cardbaseFile); } else { - System.out.println(args[0] + " appears to be an invalid path."); - System.exit(0); + throw new IllegalArgumentException(); } } else { System.out.println("No cardbase file was provided, creating a clean cardbase."); @@ -465,7 +471,7 @@ public final class CardbaseCLI { */ String sanitiseFileName(String name) { // POSIX-compliant valid filename characters - name = name.replaceAll("[^-_.A-Za-z0-9]", ""); + name = name.replaceAll("[^-_./A-Za-z0-9]", ""); // extension is not indispensable, but good practice if (!name.endsWith(".cb")) { name = name.concat(".cb"); diff --git a/src/eu/equalparts/cardbase/utils/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java index 7a58c1c..b40e518 100644 --- a/src/eu/equalparts/cardbase/utils/MTGUniverse.java +++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java @@ -40,8 +40,7 @@ public final class MTGUniverse { /** * A cache of CardSets to avoid querying the server many times for the same information. */ - private static ArrayList<CardSetInformation> cardSets; - + private static List<CardSetInformation> cardSets; /** * A cache of {@code FullCardSets} to avoid querying the server many times for the same information. */ @@ -110,13 +109,13 @@ public final class MTGUniverse { /** * @return a list of all card sets in the form of {@code CardSet} objects. */ - public static ArrayList<CardSetInformation> getCardSetList() { + public static List<CardSetInformation> getCardSetList() { // if the list isn't cached, fetch and cache it if (cardSets == null) { try { cardSets = JSON.mapper.readValue(new URL(BASE_DATA_URL + "SetList.json"), new TypeReference<ArrayList<CardSetInformation>>() {}); } catch (Exception e) { - System.out.println("Error: could not fetch/parse set code list from upstream, loading fallback json..."); + System.out.println("Error: could not fetch or parse set code list from upstream, using fallback json..."); e.printStackTrace(); try { diff --git a/test/eu/equalparts/cardbase/CardbaseSortTest.java b/test/eu/equalparts/cardbase/CardbaseSortTest.java index 298872a..44b68be 100644 --- a/test/eu/equalparts/cardbase/CardbaseSortTest.java +++ b/test/eu/equalparts/cardbase/CardbaseSortTest.java @@ -30,7 +30,7 @@ public class CardbaseSortTest { @BeforeClass public static void setUpBeforeClass() throws Exception { ObjectMapper mapper = new ObjectMapper(); - testCards = mapper.readValue(CardbaseSortTest.class.getResourceAsStream("testcards.json"), new TypeReference<List<Card>>() {}); + testCards = mapper.readValue(CardbaseSortTest.class.getResourceAsStream("/testcards.json"), new TypeReference<List<Card>>() {}); } @AfterClass diff --git a/test/eu/equalparts/cardbase/CardbaseTest.java b/test/eu/equalparts/cardbase/CardbaseTest.java index 48a0bb4..82b05ba 100644 --- a/test/eu/equalparts/cardbase/CardbaseTest.java +++ b/test/eu/equalparts/cardbase/CardbaseTest.java @@ -3,6 +3,7 @@ package eu.equalparts.cardbase; import static org.junit.Assert.*; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Field; import java.util.Map; @@ -13,6 +14,7 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; @@ -21,8 +23,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import eu.equalparts.cardbase.Cardbase; import eu.equalparts.cardbase.cards.Card; -import eu.equalparts.cardbase.testutils.TestFile; -import eu.equalparts.cardbase.testutils.TestUtils; /** * TODO deck functionality needs to be built into these. @@ -38,6 +38,9 @@ public class CardbaseTest { @Rule public final ExpectedException exception = ExpectedException.none(); + @Rule + public final TemporaryFolder tempFolder = new TemporaryFolder(); + @BeforeClass public static void setUpBeforeClass() throws Exception { ObjectMapper mapper = new ObjectMapper(); @@ -63,7 +66,7 @@ public class CardbaseTest { @Test public void fileCardbaseIsInitialised() throws Exception { - uut = new Cardbase(new File(getClass().getResource("/testbase.cb").toURI())); + uut = new Cardbase(new File(getClass().getResource("/testbase.cb").getFile())); assertEquals("Card collection contains the wrong number of card entries.", 6, uut.getCards().size()); @@ -102,20 +105,33 @@ public class CardbaseTest { @Test public void loadFileDoesNotExist() throws Exception { + File notAFile = tempFolder.newFile(); + tempFolder.delete(); + exception.expect(IOException.class); - uut = new Cardbase(new File("not a file")); + uut = new Cardbase(notAFile); } @Test public void loadFileHasWrongStructure() throws Exception { + File wrongStructure = tempFolder.newFile("wrongStructure.json"); + try (FileWriter writer = new FileWriter(wrongStructure)) { + writer.write("{\"field1\":\"content\",\"field2\":50,\"list\":[10,20,30]}"); + } + exception.expect(JsonMappingException.class); - uut = new Cardbase(new File(getClass().getResource("/testcards.json").toURI())); + uut = new Cardbase(wrongStructure); } @Test public void loadFileIsNotJson() throws Exception { + File notJson = tempFolder.newFile("wrongStructure.json"); + try (FileWriter writer = new FileWriter(notJson)) { + writer.write("This is a file which does not contain valid JSON."); + } + exception.expect(JsonParseException.class); - uut = new Cardbase(new File(getClass().getResource("/notjson.txt").toURI())); + uut = new Cardbase(notJson); } /*********************************************************************************** @@ -123,20 +139,19 @@ public class CardbaseTest { ***********************************************************************************/ @Test public void cardbaseIsSaved() throws Exception { - try (TestFile testFile = TestUtils.createValidTestFile("savetest.cb")) { - uut.writeCollection(testFile); - uut = new Cardbase(testFile); - assertEquals("Cardbase should contain no cards.", 0, uut.getCards().size()); + File testFile = tempFolder.newFile("saveTest.cb"); + uut.writeCollection(testFile); + uut = new Cardbase(testFile); + assertEquals("Cardbase should contain no cards.", 0, uut.getCards().size()); - uut.addCard(testCard); + uut.addCard(testCard); - uut.writeCollection(testFile); - uut = new Cardbase(testFile); - assertEquals("Cardbase should contain one card.", 1, uut.getCards().size()); - Card card = uut.getCard("M15", "281"); - assertNotNull("Cardbase should contain a Shivan Dragon.", card); - assertEquals("Cardbase should contain only one Shivan Dragon", new Integer(1), card.count); - } + uut.writeCollection(testFile); + uut = new Cardbase(testFile); + assertEquals("Cardbase should contain one card.", 1, uut.getCards().size()); + Card card = uut.getCard("M15", "281"); + assertNotNull("Cardbase should contain a Shivan Dragon.", card); + assertEquals("Cardbase should contain only one Shivan Dragon.", new Integer(1), card.count); } /* @@ -144,11 +159,10 @@ public class CardbaseTest { */ @Test public void saveFileCannotBeWrittenTo() throws Exception { - try (TestFile testFile = TestUtils.createValidTestFile("savetest.cb")) { - testFile.setWritable(false); - exception.expect(IOException.class); - uut.writeCollection(testFile); - } + File testFile = tempFolder.newFile("saveTest.cb"); + testFile.setWritable(false); + exception.expect(IOException.class); + uut.writeCollection(testFile); } @Test @@ -267,7 +281,7 @@ public class CardbaseTest { @Test public void correctCardCollectionIsReturnedByGetter() throws Exception { - uut = new Cardbase(new File(getClass().getResource("/testbase.cb").toURI())); + uut = new Cardbase(new File(getClass().getResource("/testbase.cb").getFile())); Map<Integer, Card> cards = new ObjectMapper().readValue(getClass().getResourceAsStream("/testbase.cb"), new TypeReference<Map<Integer, Card>>() {}); assertTrue("Not all cards were returned by the getter.", uut.getCards().containsAll(cards.values())); diff --git a/test/eu/equalparts/cardbase/cli/CardbaseCLITest.java b/test/eu/equalparts/cardbase/cli/CardbaseCLITest.java index 0cc0cbc..e84c13c 100644 --- a/test/eu/equalparts/cardbase/cli/CardbaseCLITest.java +++ b/test/eu/equalparts/cardbase/cli/CardbaseCLITest.java @@ -2,8 +2,8 @@ package eu.equalparts.cardbase.cli; import static org.junit.Assert.*; -import java.io.IOException; -import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.PrintStream; import java.util.Scanner; @@ -11,27 +11,32 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; import com.fasterxml.jackson.databind.ObjectMapper; import eu.equalparts.cardbase.cards.Card; -import eu.equalparts.cardbase.testutils.TestFile; -import eu.equalparts.cardbase.testutils.TestUtils; public class CardbaseCLITest { private CardbaseCLI uut; - private static StringBuilder output = new StringBuilder(); + + private ByteArrayOutputStream testOutput; + private final PrintStream console = System.out; + private final String EOL = System.getProperty("line.separator"); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); @BeforeClass public static void setUpBeforeClass() throws Exception { -// System.setOut(new PrintStream(new OutputStream() { -// @Override -// public void write(int b) throws IOException { -// output.append((char) b); -// } -// }, true)); + } @AfterClass @@ -41,6 +46,7 @@ public class CardbaseCLITest { @Before public void setUp() throws Exception { uut = new CardbaseCLI(); + testOutput = new ByteArrayOutputStream(); } @After @@ -89,7 +95,7 @@ public class CardbaseCLITest { @Test public void leadingTrailingAndIntermediaryWhiteSpace() throws Exception { - String[] processedInput = uut.sanitiseInput(" this was a triumph "); + String[] processedInput = uut.sanitiseInput(" \t this \twas \t \t a triumph \t\t "); assertEquals("Wrong array length.", 4, processedInput.length); assertEquals("this", processedInput[0]); @@ -136,27 +142,247 @@ public class CardbaseCLITest { public void nameWithIllegalCharacters() throws Exception { String processedName = uut.sanitiseFileName("f1lEnämẽ\n\t\"--._-//? \t^|#ŧ@fhw9vLL'''"); - assertEquals("f1lEnm--._-fhw9vLL.cb", processedName); + assertEquals("f1lEnm--._-//fhw9vLL.cb", processedName); } /*********************************************************************************** - * Write method tests, happy path + * Constructor tests, happy path + ***********************************************************************************/ + @Test + public void instantiationWithoutArguments() throws Exception { + uut = new CardbaseCLI(); + + assertEquals("Cardbase contains the wrong number of card entries.", 0, uut.cardbase.getCards().size()); + } + + @Test + public void instantiationWithCardbaseFile() throws Exception { + uut = new CardbaseCLI(getClass().getResource("/testbase.cb").getFile()); + + assertEquals("Cardbase contains the wrong number of card entries.", 6, uut.cardbase.getCards().size()); + + class CardInfo { + String setCode, number; + Integer count; + public CardInfo(String setCode, String number, Integer count) { + this.setCode = setCode; + this.number = number; + this.count = count; + } + } + CardInfo[] testCards = new CardInfo[] { + new CardInfo("M12", "34", 2), + new CardInfo("FRF", "129", 8), + new CardInfo("M12", "26", 1), + new CardInfo("FRF", "127", 1), + new CardInfo("FRF", "128", 1), + new CardInfo("M12", "152", 1)}; + + for (CardInfo ci : testCards) { + Card card = uut.cardbase.getCard(ci.setCode, ci.number); + assertNotNull("Missing card, set " + ci.setCode + ", " + ci.number, card); + assertEquals("Wrong card count, set " + ci.setCode + ", " + ci.number, ci.count, card.count); + } + } + + /* + * Edge cases + */ + @Test + public void instantiationWithInvalidArguments() throws Exception { + File notAFile = tempFolder.newFile(); + tempFolder.delete(); + + exception.expect(IllegalArgumentException.class); + uut = new CardbaseCLI(notAFile.getAbsolutePath()); + } + + @Test + public void instantiationWithEmptyArguments() throws Exception { + uut = new CardbaseCLI(""); + + assertEquals("Cardbase contains the wrong number of card entries.", 0, uut.cardbase.getCards().size()); + } + + /*********************************************************************************** + * help() tests, happy path + ***********************************************************************************/ + @Test + public void helpInformationIsPrinted() throws Exception { + try (Scanner scanner = new Scanner(getClass().getResourceAsStream("/help_en"))) { + String help = scanner.useDelimiter("\\Z").next(); + + try { + System.setOut(new PrintStream(testOutput)); + uut.help(); + } finally { + System.setOut(console); + } + assertEquals(help + EOL, testOutput.toString()); + } + } + + /*********************************************************************************** + * write() tests, happy path ***********************************************************************************/ @Test public void writeCardbaseToSpecifiedFile() throws Exception { + File testFile = tempFolder.newFile("saveTest.cb"); + try (Scanner scanner = new Scanner(getClass().getResourceAsStream("/shivandragon.json")); - TestFile testFile = TestUtils.createValidTestFile("testsave.cb"); Scanner scanner2 = new Scanner(testFile)) { - String cardJSON = scanner.useDelimiter("\\Z").next(); Card testCard = new ObjectMapper().readValue(cardJSON, Card.class); testCard.count = 1; uut.cardbase.addCard(testCard); + + try { + System.setOut(new PrintStream(testOutput)); + uut.write(testFile.getAbsolutePath()); + } finally { + System.setOut(console); + } + + String save = scanner2.useDelimiter("\\Z").next(); + assertTrue(save.contains(cardJSON)); + assertEquals("Cardbase was saved to \"" + testFile.getAbsolutePath() + "\". " + + "Subsequent writes will be done to this same file unless otherwise requested." + EOL, testOutput.toString()); + } + } + + @Test + public void specifiedFileIsSubsequentlyUsedByDefault() throws Exception { + File testFile = tempFolder.newFile("saveTest.cb"); + + try (Scanner scanner = new Scanner(getClass().getResourceAsStream("/shivandragon.json")); + Scanner scanner2 = new Scanner(testFile)) { uut.write(testFile.getAbsolutePath()); + String cardJSON = scanner.useDelimiter("\\Z").next(); + Card testCard = new ObjectMapper().readValue(cardJSON, Card.class); + testCard.count = 1; + uut.cardbase.addCard(testCard); + + try { + System.setOut(new PrintStream(testOutput)); + uut.write(); + } finally { + System.setOut(console); + } + String save = scanner2.useDelimiter("\\Z").next(); assertTrue(save.contains(cardJSON)); - + + assertEquals("Cardbase was saved to \"" + testFile.getAbsolutePath() + "\". " + + "Subsequent writes will be done to this same file unless otherwise requested." + EOL, testOutput.toString()); + } + } + + @Test + public void noFileIsProvidedAndNoDefaultIsAvailable() throws Exception { + try { + System.setOut(new PrintStream(testOutput)); + uut.write(); + } finally { + System.setOut(console); } + assertEquals("Please provide a file name." + EOL, testOutput.toString()); } + + /* + * Edge cases + */ + @Test + public void pathProvidedIsDirectory() throws Exception { + File directory = tempFolder.newFolder("testdirectory.cb"); + try { + System.setOut(new PrintStream(testOutput)); + uut.write(directory.getAbsolutePath()); + } finally { + System.setOut(console); + } + assertEquals("Could not write to \"" + directory.getAbsolutePath() + "\"." + EOL, testOutput.toString()); + } + + /*********************************************************************************** + * version() tests, happy path + ***********************************************************************************/ + @Test + public void correctVersionIsPrinted() throws Exception { + try { + System.setOut(new PrintStream(testOutput)); + uut.version(); + } finally { + System.setOut(console); + } + assertEquals("Cardbase v" + CardbaseCLI.VERSION + EOL, testOutput.toString()); + } + + /*********************************************************************************** + * exit() tests, happy path + ***********************************************************************************/ + @Test + public void exitFlagIsRaised() throws Exception { + uut.exit(); + + assertEquals("Incorrect state for exit flag.", true, uut.exit); + } + + @Test + public void saveReminderIsPrintedIfPromptFlagIsRaised() throws Exception { + uut.savePrompt = true; + try { + System.setOut(new PrintStream(testOutput)); + uut.exit(); + } finally { + System.setOut(console); + } + assertEquals("Don't forget to save. If you really wish to quit without saving, type \"exit\" again." + EOL, testOutput.toString()); + assertEquals("Incorrect state for exit flag.", false, uut.exit); + } + + @Test + public void exitFlagIsRaisedAfterSavePromptIsAcknowledged() throws Exception { + uut.savePrompt = true; + + uut.exit(); + + assertEquals("Incorrect state for exit flag.", false, uut.exit); + assertEquals("Incorrect state for save flag.", false, uut.savePrompt); + + uut.exit(); + + assertEquals("Incorrect state for exit flag.", true, uut.exit); + } + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/eu/equalparts/cardbase/testutils/TestFile.java b/test/eu/equalparts/cardbase/testutils/TestFile.java deleted file mode 100644 index ac666a9..0000000 --- a/test/eu/equalparts/cardbase/testutils/TestFile.java +++ /dev/null @@ -1,23 +0,0 @@ -package eu.equalparts.cardbase.testutils; - -import java.io.File; - -public class TestFile extends File implements AutoCloseable { - - /** - * - */ - private static final long serialVersionUID = 3716372675161543906L; - - public TestFile(String pathname) { - super(pathname); - } - - @Override - public void close() throws Exception { - delete(); - } - - - -} diff --git a/test/eu/equalparts/cardbase/testutils/TestUtils.java b/test/eu/equalparts/cardbase/testutils/TestUtils.java deleted file mode 100644 index ba590c8..0000000 --- a/test/eu/equalparts/cardbase/testutils/TestUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package eu.equalparts.cardbase.testutils; - -public class TestUtils { - - public static TestFile createValidTestFile(String fileName) throws Exception { - TestFile testFile = new TestFile(fileName); - if (!testFile.exists()) { - if (testFile.createNewFile()) { - if (testFile.canWrite()) { - return testFile; - } else { - throw new IllegalArgumentException("Cannot write to " + testFile.getAbsolutePath() + ", aborting..."); - } - } else { - throw new IllegalArgumentException(testFile.getAbsolutePath() + " could not be created, aborting..."); - } - } else { - throw new IllegalArgumentException(testFile.getAbsolutePath() + " already exists, aborting..."); - } - } - - - -} diff --git a/test/notjson.txt b/test/notjson.txt deleted file mode 100644 index 7705955..0000000 --- a/test/notjson.txt +++ /dev/null @@ -1 +0,0 @@ -This is a file which does not contain valid JSON.
\ No newline at end of file @@ -4,6 +4,5 @@ Cardbase: * decks CardbaseCLI: - * user input sanity * command methods *
\ No newline at end of file |