├── README.md
└── katas_java
├── .gitignore
├── 01-mars-rover-movement
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── .keep
│ └── test
│ └── java
│ └── RoverTest.java
├── 02_smelly-mars-rover-refactoring
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── mars_rover
│ │ └── Rover.java
│ └── test
│ └── java
│ ├── RoverEqualityTests.java
│ ├── RoverPositionTests.java
│ ├── RoverReceivingCommandsListTests.java
│ └── RoverRotationTests.java
├── 03a-mars-rover-adding-new-feature
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── mars_rover
│ │ ├── Coordinates.java
│ │ ├── Direction.java
│ │ └── MarsRover.java
│ └── test
│ └── java
│ └── MarsRoverTest.java
├── 03b-mars-rover-adding-new-feature
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── mars_rover
│ │ ├── Command.java
│ │ ├── MarsRover.java
│ │ ├── NasaCommunicationProtocol.java
│ │ ├── command_types
│ │ ├── MovingBackwards.java
│ │ ├── MovingForward.java
│ │ ├── TurningLeft.java
│ │ ├── TurningRight.java
│ │ └── UnknownCommand.java
│ │ └── location
│ │ ├── Coordinates.java
│ │ ├── Direction.java
│ │ └── Vector.java
│ └── test
│ └── java
│ └── MarsRoverTest.java
├── 04-working-with-code-we-do-not-own
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── items
│ │ └── Item.java
│ └── test
│ └── java
│ └── GildedRoseTest.java
├── 05-refactoring-awful-inheritance-use-with-beverage-prices-kata
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── beverages
│ │ ├── Beverage.java
│ │ ├── Coffee.java
│ │ ├── CoffeeWithMilk.java
│ │ ├── CoffeeWithMilkAndCream.java
│ │ ├── HotChocolate.java
│ │ ├── HotChocolateWithCream.java
│ │ ├── Tea.java
│ │ └── TeaWithMilk.java
│ └── test
│ └── java
│ └── unit_tests
│ └── BeveragesPricingTest.java
├── 06-refactoring-to-hexagonal-architecture
├── pom.xml
├── readme.md
└── src
│ ├── main
│ └── java
│ │ └── birthdaygreetings
│ │ ├── BirthdayService.java
│ │ ├── Employee.java
│ │ └── OurDate.java
│ └── test
│ ├── java
│ └── birthdaygreetings
│ │ └── test
│ │ ├── AcceptanceTest.java
│ │ ├── EmployeeTest.java
│ │ └── OurDateTest.java
│ └── resources
│ └── employee_data.txt
├── 07-refactoring-to-hexagonal-architecture-2
├── pom.xml
├── readme.md
└── src
│ ├── main
│ └── java
│ │ └── birthdaygreetings
│ │ ├── application
│ │ ├── BirthdayService.java
│ │ └── Main.java
│ │ ├── core
│ │ ├── CannotReadEmployeesException.java
│ │ ├── Employee.java
│ │ ├── EmployeesRepository.java
│ │ ├── Greeting.java
│ │ ├── GreetingMessage.java
│ │ └── OurDate.java
│ │ └── infrastructure
│ │ └── repositories
│ │ ├── DateRepresentation.java
│ │ ├── EmployeesFile.java
│ │ └── FileEmployeesRepository.java
│ └── test
│ ├── java
│ └── test
│ │ └── birthdaygreetings
│ │ ├── application
│ │ └── BirthdayServiceAcceptanceTest.java
│ │ ├── core
│ │ ├── EmployeeTest.java
│ │ └── OurDateTest.java
│ │ ├── helpers
│ │ └── OurDateFactory.java
│ │ └── infrastructure
│ │ └── repositories
│ │ └── FileEmployeeRepositoryTest.java
│ └── resources
│ ├── employee_data.txt
│ ├── wrong_data__one-field-only.csv
│ └── wrong_data__wrong-date-format.csv
├── 08-doing-tdd-on-legacy-code
├── README.md
├── pom.xml
├── res
│ ├── example.html
│ └── example.txt
└── src
│ ├── main
│ └── java
│ │ └── text_converter
│ │ └── HtmlTextConverter.java
│ └── test
│ └── java
│ └── text_converter
│ └── .keepme
├── 09-outside-in-with-ohce-kata
├── part_1
│ ├── pom.xml
│ ├── readme.md
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── ohce
│ │ │ └── .keep
│ │ └── test
│ │ └── java
│ │ └── test
│ │ └── ohce
│ │ └── .keep
├── part_2
│ ├── pom.xml
│ ├── readme.md
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── ohce
│ │ │ └── .keep
│ │ └── test
│ │ └── java
│ │ └── test
│ │ └── ohce
│ │ └── .keep
└── readme.md
├── 10-outside-in-with-bank-kata
├── .gitignore
├── README.md
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── bank
│ │ └── .keep
│ └── test
│ └── java
│ └── bank
│ └── tests
│ ├── acceptance
│ └── PrintingAccountStatementOnConsoleTest.java
│ └── unit
│ └── .keep
└── 11-outside-in-with-unusual-spending-alert-service
├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── alert_service
│ └── .keep
└── test
└── java
└── alert_service
└── tests
├── acceptance
└── AlertingUsersWithUnusualSpendingsTest.java
└── unit
└── .keep
/README.md:
--------------------------------------------------------------------------------
1 | # Deliberate Practice Program
2 |
3 | Practice program to build up code skills through katas, readings and talks.
4 |
5 | Run before at:
6 |
7 | * Magento, Merkle, Trovit, Mango, Properati and Genially.
8 |
9 | Currently being run at:
10 |
11 | * Audiense and Aida.
12 |
--------------------------------------------------------------------------------
/katas_java/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | .classpath
3 | .project
4 | .idea
5 | *.iml
6 | target
--------------------------------------------------------------------------------
/katas_java/01-mars-rover-movement/README.md:
--------------------------------------------------------------------------------
1 | # Mars Rover implementation exercise
2 |
3 | ### Description
4 | In this exercise we'll implement from scratch part of the Mars Rover kata.
5 |
6 | This is a summary of the behavior of the rover that we'll implement:
7 |
8 | - It's located on a grid at some point with coordinates (x,y) and facing a direction encoded with a character.
9 |
10 | - The meaning of each direction character is:
11 |
12 | * ``N`` -> North
13 | * ``S`` -> South
14 | * ``E`` -> East
15 | * ``W`` -> West
16 |
17 | - The rover receives a sequence of commands (a string of characters) which are codified in the following way:
18 |
19 | * When it receives an ``f``, it moves forward one position in the direction it is facing.
20 | * When it receives a ``b``, it moves backward one position in the direction it is facing.
21 | * When it receives a ``l``, it turns left changing its direction (by one step in the direction).
22 | * When it receives a ``r``, it turns right changing its direction (by one step in the direction).
23 |
24 |
--------------------------------------------------------------------------------
/katas_java/01-mars-rover-movement/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | mars-rover-movement-tdd
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/katas_java/01-mars-rover-movement/src/main/java/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/01-mars-rover-movement/src/main/java/.keep
--------------------------------------------------------------------------------
/katas_java/01-mars-rover-movement/src/test/java/RoverTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Test;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 |
6 | public class RoverTest {
7 |
8 | @Test
9 | public void fix_and_rename_me() {
10 | assertThat(true, is(false));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/README.md:
--------------------------------------------------------------------------------
1 | Smelly Mars Rover code refactoring
2 | =============================================
3 |
4 | Smelly Mars Rover code to practice refactoring.
5 |
6 | We'll use it to learn to recognize some code smells
7 | and to fix them applying some useful refactorings.
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | smelly-mars-rover-refactoring
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/src/main/java/mars_rover/Rover.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | public class Rover {
4 |
5 | private String direction;
6 | private int y;
7 | private int x;
8 |
9 | public Rover(int x, int y, String direction) {
10 | this.direction = direction;
11 | this.y = y;
12 | this.x = x;
13 | }
14 |
15 | public void receive(String commandsSequence) {
16 | for (int i = 0; i < commandsSequence.length(); ++i) {
17 | String command = commandsSequence.substring(i, i + 1);
18 |
19 | if (command.equals("l") || command.equals("r")) {
20 |
21 | // Rotate Rover
22 | if (direction.equals("N")) {
23 | if (command.equals("r")) {
24 | direction = "E";
25 | } else {
26 | direction = "W";
27 | }
28 | } else if (direction.equals("S")) {
29 | if (command.equals("r")) {
30 | direction = "W";
31 | } else {
32 | direction = "E";
33 | }
34 | } else if (direction.equals("W")) {
35 | if (command.equals("r")) {
36 | direction = "N";
37 | } else {
38 | direction = "S";
39 | }
40 | } else {
41 | if (command.equals("r")) {
42 | direction = "S";
43 | } else {
44 | direction = "N";
45 | }
46 | }
47 | } else {
48 |
49 | // Displace Rover
50 | int displacement1 = -1;
51 |
52 | if (command.equals("f")) {
53 | displacement1 = 1;
54 | }
55 | int displacement = displacement1;
56 |
57 | if (direction.equals("N")) {
58 | y += displacement;
59 | } else if (direction.equals("S")) {
60 | y -= displacement;
61 | } else if (direction.equals("W")) {
62 | x -= displacement;
63 | } else {
64 | x += displacement;
65 | }
66 | }
67 | }
68 | }
69 |
70 | @Override
71 | public boolean equals(Object o) {
72 | if (this == o) return true;
73 | if (o == null || getClass() != o.getClass()) return false;
74 |
75 | Rover rover = (Rover) o;
76 |
77 | if (y != rover.y) return false;
78 | if (x != rover.x) return false;
79 | return direction != null ? direction.equals(rover.direction) : rover.direction == null;
80 |
81 | }
82 |
83 | @Override
84 | public int hashCode() {
85 | int result = direction != null ? direction.hashCode() : 0;
86 | result = 31 * result + y;
87 | result = 31 * result + x;
88 | return result;
89 | }
90 |
91 | @Override
92 | public String toString() {
93 | return "Rover{" +
94 | "direction='" + direction + '\'' +
95 | ", y=" + y +
96 | ", x=" + x +
97 | '}';
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverEqualityTests.java:
--------------------------------------------------------------------------------
1 | import static org.junit.Assert.*;
2 |
3 | import mars_rover.Rover;
4 | import org.junit.Test;
5 |
6 | public class RoverEqualityTests {
7 | @Test
8 | public void equalRovers() {
9 | assertEquals(new Rover(1, 1, "N"), new Rover(1, 1, "N"));
10 | }
11 |
12 | @Test
13 | public void notEqualRovers() {
14 | assertFalse(new Rover(1, 1, "N").equals(new Rover(1, 1, "S")));
15 | assertFalse(new Rover(1, 1, "N").equals(new Rover(1, 2, "N")));
16 | assertFalse(new Rover(1, 1, "N").equals(new Rover(0, 1, "N")));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverPositionTests.java:
--------------------------------------------------------------------------------
1 | import static org.junit.Assert.*;
2 |
3 | import mars_rover.Rover;
4 | import org.junit.Test;
5 |
6 | public class RoverPositionTests {
7 | @Test
8 | public void facingNorthMoveForward() {
9 | Rover rover = new Rover(0, 0, "N");
10 |
11 | rover.receive("f");
12 |
13 | assertEquals(new Rover(0, 1, "N"), rover);
14 | }
15 |
16 | @Test
17 | public void facingNorthMoveBackward() {
18 | Rover rover = new Rover(0, 0, "N");
19 |
20 | rover.receive("b");
21 |
22 | assertEquals(new Rover(0, -1, "N"), rover);
23 | }
24 |
25 | @Test
26 | public void facingSouthMoveForward() {
27 | Rover rover = new Rover(0, 0, "S");
28 |
29 | rover.receive("f");
30 |
31 | assertEquals(new Rover(0, -1, "S"), rover);
32 | }
33 |
34 | @Test
35 | public void facingSouthMoveBackward() {
36 | Rover rover = new Rover(0, 0, "S");
37 |
38 | rover.receive("b");
39 |
40 | assertEquals(new Rover(0, 1, "S"), rover);
41 | }
42 |
43 | @Test
44 | public void facingWestMoveForward() {
45 | Rover rover = new Rover(0, 0, "W");
46 |
47 | rover.receive("f");
48 |
49 | assertEquals(new Rover(-1, 0, "W"), rover);
50 | }
51 |
52 | @Test
53 | public void facingWestMoveBackward() {
54 | Rover rover = new Rover(0, 0, "W");
55 |
56 | rover.receive("b");
57 |
58 | assertEquals(new Rover(1, 0, "W"), rover);
59 | }
60 |
61 | @Test
62 | public void facingEastMoveForward() {
63 | Rover rover = new Rover(0, 0, "E");
64 |
65 | rover.receive("f");
66 |
67 | assertEquals(new Rover(1, 0, "E"), rover);
68 | }
69 |
70 | @Test
71 | public void facingEastMoveBackward() {
72 | Rover rover = new Rover(0, 0, "E");
73 |
74 | rover.receive("b");
75 |
76 | assertEquals(new Rover(-1, 0, "E"), rover);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverReceivingCommandsListTests.java:
--------------------------------------------------------------------------------
1 | import static org.junit.Assert.assertEquals;
2 |
3 | import mars_rover.Rover;
4 | import org.junit.Test;
5 |
6 | public class RoverReceivingCommandsListTests {
7 | @Test
8 | public void noCommands() {
9 | Rover rover = new Rover(0, 0, "N");
10 |
11 | rover.receive("");
12 |
13 | assertEquals(new Rover(0, 0, "N"), rover);
14 | }
15 |
16 | @Test
17 | public void twoCommands() {
18 | Rover rover = new Rover(0, 0, "N");
19 |
20 | rover.receive("lf");
21 |
22 | assertEquals(new Rover(-1, 0, "W"), rover);
23 | }
24 |
25 | @Test
26 | public void manyCommands() {
27 | Rover rover = new Rover(0, 0, "N");
28 |
29 | rover.receive("ffrbbrfflff");
30 |
31 | assertEquals(new Rover(0, 0, "E"), rover);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/katas_java/02_smelly-mars-rover-refactoring/src/test/java/RoverRotationTests.java:
--------------------------------------------------------------------------------
1 | import static org.junit.Assert.*;
2 |
3 | import mars_rover.Rover;
4 | import org.junit.Test;
5 |
6 | public class RoverRotationTests {
7 | @Test
8 | public void facingNorthRotateLeft() {
9 | Rover rover = new Rover(0, 0, "N");
10 |
11 | rover.receive("l");
12 |
13 | assertEquals(new Rover(0, 0, "W"), rover);
14 | }
15 |
16 | @Test
17 | public void facingNorthRotateRight() {
18 | Rover rover = new Rover(0, 0, "N");
19 |
20 | rover.receive("r");
21 |
22 | assertEquals(new Rover(0, 0, "E"), rover);
23 | }
24 |
25 | @Test
26 | public void facingSouthRotateLeft() {
27 | Rover rover = new Rover(0, 0, "S");
28 |
29 | rover.receive("l");
30 |
31 | assertEquals(new Rover(0, 0, "E"), rover);
32 | }
33 |
34 | @Test
35 | public void facingSouthRotateRight() {
36 | Rover rover = new Rover(0, 0, "S");
37 |
38 | rover.receive("r");
39 |
40 | assertEquals(new Rover(0, 0, "W"), rover);
41 | }
42 |
43 | @Test
44 | public void facingWestRotateLeft() {
45 | Rover rover = new Rover(0, 0, "W");
46 |
47 | rover.receive("l");
48 |
49 | assertEquals(new Rover(0, 0, "S"), rover);
50 | }
51 |
52 | @Test
53 | public void facingWestRotateRight() {
54 | Rover rover = new Rover(0, 0, "W");
55 |
56 | rover.receive("r");
57 |
58 | assertEquals(new Rover(0, 0, "N"), rover);
59 | }
60 |
61 | @Test
62 | public void facingEastRotateLeft() {
63 | Rover rover = new Rover(0, 0, "E");
64 |
65 | rover.receive("l");
66 |
67 | assertEquals(new Rover(0, 0, "N"), rover);
68 | }
69 |
70 | @Test
71 | public void facingEastRotateRight() {
72 | Rover rover = new Rover(0, 0, "E");
73 |
74 | rover.receive("r");
75 |
76 | assertEquals(new Rover(0, 0, "S"), rover);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/README.md:
--------------------------------------------------------------------------------
1 | # Mars Rover: adding a feature to existing code
2 |
3 | We'll practice adding a new feature to an existing code base.
4 |
5 | We'll have to refactor a bit first to make the change easy.
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | mars-rover-adding-new-feature
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/Coordinates.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | class Coordinates {
4 |
5 | private final int x;
6 | private final int y;
7 |
8 | Coordinates(int x, int y) {
9 | this.x = x;
10 | this.y = y;
11 | }
12 |
13 | Coordinates incrementX(int delta) {
14 | return new Coordinates(x + delta, y);
15 | }
16 |
17 | Coordinates incrementY(int delta) {
18 | return new Coordinates(x, y + delta);
19 | }
20 |
21 | @Override
22 | public String toString() {
23 | return "Coordinates{" +
24 | "x=" + x +
25 | ", y=" + y +
26 | '}';
27 | }
28 |
29 | @Override
30 | public boolean equals(Object o) {
31 | if (this == o) return true;
32 | if (o == null || getClass() != o.getClass()) return false;
33 |
34 | Coordinates that = (Coordinates) o;
35 |
36 | if (x != that.x) return false;
37 | return y == that.y;
38 | }
39 |
40 | @Override
41 | public int hashCode() {
42 | int result = x;
43 | result = 31 * result + y;
44 | return result;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/Direction.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | enum Direction {
4 | North {
5 | public Direction turnRight() {
6 | return East;
7 | }
8 |
9 | public Direction turnLeft() {
10 | return West;
11 | }
12 |
13 | public Coordinates moveForward(Coordinates coordinates, int delta) {
14 | return coordinates.incrementY(delta);
15 | }
16 |
17 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
18 | return coordinates.incrementY(-delta);
19 | }
20 | },
21 | South {
22 | public Direction turnRight() {
23 | return West;
24 | }
25 |
26 | public Direction turnLeft() {
27 | return East;
28 | }
29 |
30 | public Coordinates moveForward(Coordinates coordinates, int delta) {
31 | return coordinates.incrementY(-delta);
32 | }
33 |
34 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
35 | return coordinates.incrementY(delta);
36 | }
37 | },
38 | East {
39 | public Direction turnRight() {
40 | return South;
41 | }
42 |
43 | public Direction turnLeft() {
44 | return North;
45 | }
46 |
47 | public Coordinates moveForward(Coordinates coordinates, int delta) {
48 | return coordinates.incrementX(delta);
49 | }
50 |
51 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
52 | return coordinates.incrementX(-delta);
53 | }
54 | },
55 | West {
56 | public Direction turnRight() {
57 | return North;
58 | }
59 |
60 | public Direction turnLeft() {
61 | return South;
62 | }
63 |
64 | public Coordinates moveForward(Coordinates coordinates, int delta) {
65 | return coordinates.incrementX(-delta);
66 | }
67 |
68 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
69 | return coordinates.incrementX(delta);
70 | }
71 | };
72 |
73 | static public Direction parse(String directionAsString) {
74 | if (directionAsString.equals("N")) {
75 | return North;
76 | } else if (directionAsString.equals("E")) {
77 | return East;
78 | } else if (directionAsString.equals("S")) {
79 | return South;
80 | } else {
81 | return West;
82 | }
83 | }
84 |
85 | abstract public Direction turnRight();
86 |
87 | abstract public Direction turnLeft();
88 |
89 | abstract public Coordinates moveForward(Coordinates coordinates, int delta);
90 |
91 | abstract public Coordinates moveBackwards(Coordinates coordinates, int delta);
92 | }
93 |
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/src/main/java/mars_rover/MarsRover.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | public class MarsRover {
4 |
5 | private static final int MOVEMENT_DELTA = 1;
6 | private Coordinates coordinates;
7 | private Direction direction;
8 |
9 | public MarsRover(int x, int y, String direction) {
10 | this.coordinates = new Coordinates(x, y);
11 | this.direction = Direction.parse(direction);
12 | }
13 |
14 | public void receive(String commandsSequence) {
15 | for (String command : commandsSequence.split("")) {
16 | executeCommand(command);
17 | }
18 | }
19 |
20 | private void executeCommand(String command) {
21 | if (command.equals("r")) {
22 | direction = direction.turnRight();
23 | } else if (command.equals("l")) {
24 | direction = direction.turnLeft();
25 | } else if (command.equals("f")) {
26 | coordinates = direction.moveForward(coordinates, MOVEMENT_DELTA);
27 | } else if (command.equals("b")) {
28 | coordinates = direction.moveBackwards(coordinates, MOVEMENT_DELTA);
29 | }
30 | }
31 |
32 | @Override
33 | public boolean equals(Object o) {
34 | if (this == o) return true;
35 | if (!(o instanceof MarsRover)) return false;
36 |
37 | MarsRover marsRover = (MarsRover) o;
38 |
39 | if (coordinates != null ? !coordinates.equals(marsRover.coordinates) : marsRover.coordinates != null)
40 | return false;
41 | return direction == marsRover.direction;
42 |
43 | }
44 |
45 | @Override
46 | public int hashCode() {
47 | int result = coordinates != null ? coordinates.hashCode() : 0;
48 | result = 31 * result + (direction != null ? direction.hashCode() : 0);
49 | return result;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "MarsRover{" +
55 | "coordinates=" + coordinates +
56 | ", direction=" + direction +
57 | '}';
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/katas_java/03a-mars-rover-adding-new-feature/src/test/java/MarsRoverTest.java:
--------------------------------------------------------------------------------
1 | import mars_rover.MarsRover;
2 | import org.junit.Test;
3 |
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import static org.hamcrest.core.Is.is;
6 |
7 | public class MarsRoverTest {
8 |
9 | @Test
10 | public void does_nothing_when_receiving_empty_commands_sequence() {
11 | MarsRover marsRover = new MarsRover(0, 0, "N");
12 |
13 | marsRover.receive("");
14 |
15 | assertThat(marsRover, is(new MarsRover(0, 0, "N")));
16 | }
17 |
18 | @Test
19 | public void turns_right_when_pointing_north() {
20 | MarsRover marsRover = new MarsRover(0, 0, "N");
21 |
22 | marsRover.receive("r");
23 |
24 | assertThat(marsRover, is(new MarsRover(0, 0, "E")));
25 | }
26 |
27 | @Test
28 | public void turns_right_when_pointing_east() {
29 | MarsRover marsRover = new MarsRover(0, 0, "E");
30 |
31 | marsRover.receive("r");
32 |
33 | assertThat(marsRover, is(new MarsRover(0, 0, "S")));
34 | }
35 |
36 | @Test
37 | public void turns_right_when_pointing_south() {
38 | MarsRover marsRover = new MarsRover(0, 0, "S");
39 |
40 | marsRover.receive("r");
41 |
42 | assertThat(marsRover, is(new MarsRover(0, 0, "W")));
43 | }
44 |
45 | @Test
46 | public void turns_right_when_pointing_west() {
47 | MarsRover marsRover = new MarsRover(0, 0, "W");
48 |
49 | marsRover.receive("r");
50 |
51 | assertThat(marsRover, is(new MarsRover(0, 0, "N")));
52 | }
53 |
54 | @Test
55 | public void turns_left_when_pointing_north() {
56 | MarsRover marsRover = new MarsRover(0, 0, "N");
57 |
58 | marsRover.receive("l");
59 |
60 | assertThat(marsRover, is(new MarsRover(0, 0, "W")));
61 | }
62 |
63 | @Test
64 | public void turns_left_when_pointing_west() {
65 | MarsRover marsRover = new MarsRover(0, 0, "W");
66 |
67 | marsRover.receive("l");
68 |
69 | assertThat(marsRover, is(new MarsRover(0, 0, "S")));
70 | }
71 |
72 | @Test
73 | public void turns_left_when_pointing_south() {
74 | MarsRover marsRover = new MarsRover(0, 0, "S");
75 |
76 | marsRover.receive("l");
77 |
78 | assertThat(marsRover, is(new MarsRover(0, 0, "E")));
79 | }
80 |
81 | @Test
82 | public void turns_left_when_pointing_east() {
83 | MarsRover marsRover = new MarsRover(0, 0, "E");
84 |
85 | marsRover.receive("l");
86 |
87 | assertThat(marsRover, is(new MarsRover(0, 0, "N")));
88 | }
89 |
90 | @Test
91 | public void moves_forward_when_pointing_north() {
92 | MarsRover marsRover = new MarsRover(5, 4, "N");
93 |
94 | marsRover.receive("f");
95 |
96 | assertThat(marsRover, is(new MarsRover(5, 5, "N")));
97 | }
98 |
99 | @Test
100 | public void moves_forward_when_pointing_east() {
101 | MarsRover marsRover = new MarsRover(5, 4, "E");
102 |
103 | marsRover.receive("f");
104 |
105 | assertThat(marsRover, is(new MarsRover(6, 4, "E")));
106 | }
107 |
108 | @Test
109 | public void moves_forward_when_pointing_south() {
110 | MarsRover marsRover = new MarsRover(5, 4, "S");
111 |
112 | marsRover.receive("f");
113 |
114 | assertThat(marsRover, is(new MarsRover(5, 3, "S")));
115 | }
116 |
117 | @Test
118 | public void moves_forward_when_pointing_west() {
119 | MarsRover marsRover = new MarsRover(5, 4, "W");
120 |
121 | marsRover.receive("f");
122 |
123 | assertThat(marsRover, is(new MarsRover(4, 4, "W")));
124 | }
125 |
126 | @Test
127 | public void moves_backward_when_pointing_north() {
128 | MarsRover marsRover = new MarsRover(5, 4, "N");
129 |
130 | marsRover.receive("b");
131 |
132 | assertThat(marsRover, is(new MarsRover(5, 3, "N")));
133 | }
134 |
135 | @Test
136 | public void moves_backward_when_pointing_east() {
137 | MarsRover marsRover = new MarsRover(5, 4, "E");
138 |
139 | marsRover.receive("b");
140 |
141 | assertThat(marsRover, is(new MarsRover(4, 4, "E")));
142 | }
143 |
144 | @Test
145 | public void moves_backward_when_pointing_south() {
146 | MarsRover marsRover = new MarsRover(5, 4, "S");
147 |
148 | marsRover.receive("b");
149 |
150 | assertThat(marsRover, is(new MarsRover(5, 5, "S")));
151 | }
152 |
153 | @Test
154 | public void moves_backward_when_pointing_west() {
155 | MarsRover marsRover = new MarsRover(5, 4, "W");
156 |
157 | marsRover.receive("b");
158 |
159 | assertThat(marsRover, is(new MarsRover(6, 4, "W")));
160 | }
161 |
162 | @Test
163 | public void receives_multiple_commands() {
164 | MarsRover marsRover = new MarsRover(7, 4, "E");
165 |
166 | marsRover.receive("fr");
167 |
168 | assertThat(marsRover, is(new MarsRover(8, 4, "S")));
169 | }
170 |
171 | @Test
172 | public void ignores_unknown_commands() {
173 | MarsRover marsRover = new MarsRover(7, 4, "E");
174 |
175 | marsRover.receive("*");
176 |
177 | assertThat(marsRover, is(new MarsRover(7, 4, "E")));
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/README.md:
--------------------------------------------------------------------------------
1 | # Mars Rover: adding a feature to existing code (I)
2 |
3 | After:
4 |
5 | 1. Using command pattern to separate message interpretation (command creation) from command execution.
6 | 2. Extracting message interpretation responsibility to a new class.
7 |
8 | Now we're closer to being able to add the new feature easily.
9 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | mars-rover-adding-new-feature-b
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/Command.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | import mars_rover.location.Vector;
4 |
5 | public interface Command {
6 | Vector execute(Vector vector);
7 | }
8 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/MarsRover.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | import mars_rover.location.Vector;
4 |
5 | import java.util.List;
6 |
7 | public class MarsRover {
8 |
9 | private static final int DISPLACEMENT = 1;
10 | private final NasaCommunicationProtocol communicationProtocol;
11 | private Vector vector;
12 |
13 | public MarsRover(int x, int y, String direction) {
14 | this.vector = Vector.create(x, y, direction);
15 | communicationProtocol = new NasaCommunicationProtocol();
16 | }
17 |
18 | public void receive(String message) {
19 | execute(createCommands(message));
20 | }
21 |
22 | private void execute(List commands) {
23 | for (Command command : commands) {
24 | vector = command.execute(vector);
25 | }
26 | }
27 |
28 | private List createCommands(String message) {
29 | return communicationProtocol.createCommands(message, DISPLACEMENT);
30 | }
31 |
32 | @Override
33 | public boolean equals(Object o) {
34 | if (this == o) return true;
35 | if (o == null || getClass() != o.getClass()) return false;
36 |
37 | MarsRover marsRover = (MarsRover) o;
38 |
39 | return vector != null ? vector.equals(marsRover.vector) : marsRover.vector == null;
40 | }
41 |
42 | @Override
43 | public int hashCode() {
44 | return vector != null ? vector.hashCode() : 0;
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return "MarsRover{" +
50 | "vector=" + vector +
51 | '}';
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/NasaCommunicationProtocol.java:
--------------------------------------------------------------------------------
1 | package mars_rover;
2 |
3 | import mars_rover.command_types.*;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | class NasaCommunicationProtocol {
9 | List createCommands(String message, int displacement) {
10 | List commands = new ArrayList<>();
11 | for (String commandRepresentation : parseMessage(message)) {
12 | commands.add(createCommand(commandRepresentation, displacement));
13 | }
14 | return commands;
15 | }
16 |
17 | private String[] parseMessage(String message) {
18 | return message.split("");
19 | }
20 |
21 | private Command createCommand(String commandRepresentation, int displacement) {
22 |
23 | if (commandRepresentation.equals("r")) {
24 | return new TurningRight();
25 | } else if (commandRepresentation.equals("l")) {
26 | return new TurningLeft();
27 | } else if (commandRepresentation.equals("f")) {
28 | return new MovingForward(displacement);
29 | } else if (commandRepresentation.equals("b")) {
30 | return new MovingBackwards(displacement);
31 | } else {
32 | return new UnknownCommand();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/MovingBackwards.java:
--------------------------------------------------------------------------------
1 | package mars_rover.command_types;
2 |
3 | import mars_rover.Command;
4 | import mars_rover.location.Vector;
5 |
6 | public class MovingBackwards implements Command {
7 | private int delta;
8 |
9 | public MovingBackwards(int delta) {
10 | this.delta = delta;
11 | }
12 |
13 | @Override
14 | public Vector execute(Vector vector) {
15 | return vector.moveBackwards(delta);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/MovingForward.java:
--------------------------------------------------------------------------------
1 | package mars_rover.command_types;
2 |
3 | import mars_rover.Command;
4 | import mars_rover.location.Vector;
5 |
6 | public class MovingForward implements Command {
7 | private int delta;
8 |
9 | public MovingForward(int delta) {
10 | this.delta = delta;
11 | }
12 |
13 | @Override
14 | public Vector execute(Vector vector) {
15 | return vector.moveForward(delta);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/TurningLeft.java:
--------------------------------------------------------------------------------
1 | package mars_rover.command_types;
2 |
3 | import mars_rover.Command;
4 | import mars_rover.location.Vector;
5 |
6 | public class TurningLeft implements Command {
7 | @Override
8 | public Vector execute(Vector vector) {
9 | return vector.turnLeft();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/TurningRight.java:
--------------------------------------------------------------------------------
1 | package mars_rover.command_types;
2 |
3 | import mars_rover.Command;
4 | import mars_rover.location.Vector;
5 |
6 | public class TurningRight implements Command {
7 | @Override
8 | public Vector execute(Vector vector) {
9 | return vector.turnRight();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/command_types/UnknownCommand.java:
--------------------------------------------------------------------------------
1 | package mars_rover.command_types;
2 |
3 | import mars_rover.Command;
4 | import mars_rover.location.Vector;
5 |
6 | public class UnknownCommand implements Command {
7 | @Override
8 | public Vector execute(Vector vector) {
9 | return vector;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Coordinates.java:
--------------------------------------------------------------------------------
1 | package mars_rover.location;
2 |
3 | class Coordinates {
4 |
5 | private final int x;
6 | private final int y;
7 |
8 | Coordinates(int x, int y) {
9 | this.x = x;
10 | this.y = y;
11 | }
12 |
13 | Coordinates incrementX(int delta) {
14 | return new Coordinates(x + delta, y);
15 | }
16 |
17 | Coordinates incrementY(int delta) {
18 | return new Coordinates(x, y + delta);
19 | }
20 |
21 | @Override
22 | public String toString() {
23 | return "Coordinates{" +
24 | "x=" + x +
25 | ", y=" + y +
26 | '}';
27 | }
28 |
29 | @Override
30 | public boolean equals(Object o) {
31 | if (this == o) return true;
32 | if (o == null || getClass() != o.getClass()) return false;
33 |
34 | Coordinates that = (Coordinates) o;
35 |
36 | if (x != that.x) return false;
37 | return y == that.y;
38 | }
39 |
40 | @Override
41 | public int hashCode() {
42 | int result = x;
43 | result = 31 * result + y;
44 | return result;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Direction.java:
--------------------------------------------------------------------------------
1 | package mars_rover.location;
2 |
3 | enum Direction {
4 | North {
5 | public Direction turnRight() {
6 | return East;
7 | }
8 |
9 | public Direction turnLeft() {
10 | return West;
11 | }
12 |
13 | public Coordinates moveForward(Coordinates coordinates, int delta) {
14 | return coordinates.incrementY(delta);
15 | }
16 |
17 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
18 | return coordinates.incrementY(-delta);
19 | }
20 | },
21 | South {
22 | public Direction turnRight() {
23 | return West;
24 | }
25 |
26 | public Direction turnLeft() {
27 | return East;
28 | }
29 |
30 | public Coordinates moveForward(Coordinates coordinates, int delta) {
31 | return coordinates.incrementY(-delta);
32 | }
33 |
34 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
35 | return coordinates.incrementY(delta);
36 | }
37 | },
38 | East {
39 | public Direction turnRight() {
40 | return South;
41 | }
42 |
43 | public Direction turnLeft() {
44 | return North;
45 | }
46 |
47 | public Coordinates moveForward(Coordinates coordinates, int delta) {
48 | return coordinates.incrementX(delta);
49 | }
50 |
51 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
52 | return coordinates.incrementX(-delta);
53 | }
54 | },
55 | West {
56 | public Direction turnRight() {
57 | return North;
58 | }
59 |
60 | public Direction turnLeft() {
61 | return South;
62 | }
63 |
64 | public Coordinates moveForward(Coordinates coordinates, int delta) {
65 | return coordinates.incrementX(-delta);
66 | }
67 |
68 | public Coordinates moveBackwards(Coordinates coordinates, int delta) {
69 | return coordinates.incrementX(delta);
70 | }
71 | };
72 |
73 | static public Direction parse(String directionAsString) {
74 | if (directionAsString.equals("N")) {
75 | return North;
76 | } else if (directionAsString.equals("E")) {
77 | return East;
78 | } else if (directionAsString.equals("S")) {
79 | return South;
80 | } else {
81 | return West;
82 | }
83 | }
84 |
85 | abstract public Direction turnRight();
86 |
87 | abstract public Direction turnLeft();
88 |
89 | abstract public Coordinates moveForward(Coordinates coordinates, int delta);
90 |
91 | abstract public Coordinates moveBackwards(Coordinates coordinates, int delta);
92 | }
93 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/main/java/mars_rover/location/Vector.java:
--------------------------------------------------------------------------------
1 | package mars_rover.location;
2 |
3 | public class Vector {
4 |
5 | private final Coordinates coordinates;
6 | private final Direction direction;
7 |
8 | public static Vector create(int x, int y, String direction) {
9 | return new Vector(new Coordinates(x, y), Direction.parse(direction));
10 | }
11 |
12 | private Vector(Coordinates coordinates, Direction direction) {
13 | this.coordinates = coordinates;
14 | this.direction = direction;
15 | }
16 |
17 | public Vector turnRight() {
18 | return new Vector(coordinates, direction.turnRight());
19 | }
20 |
21 | public Vector turnLeft() {
22 | return new Vector(coordinates, direction.turnLeft());
23 | }
24 |
25 | public Vector moveForward(int delta) {
26 | return new Vector(direction.moveForward(coordinates, delta), direction);
27 | }
28 |
29 | public Vector moveBackwards(int delta) {
30 | return new Vector(direction.moveBackwards(coordinates, delta), direction);
31 | }
32 |
33 | @Override
34 | public boolean equals(Object o) {
35 | if (this == o) return true;
36 | if (o == null || getClass() != o.getClass()) return false;
37 |
38 | Vector vector = (Vector) o;
39 |
40 | if (coordinates != null ? !coordinates.equals(vector.coordinates) : vector.coordinates != null) return false;
41 | return direction == vector.direction;
42 | }
43 |
44 | @Override
45 | public int hashCode() {
46 | int result = coordinates != null ? coordinates.hashCode() : 0;
47 | result = 31 * result + (direction != null ? direction.hashCode() : 0);
48 | return result;
49 | }
50 |
51 | @Override
52 | public String toString() {
53 | return "Vector{" +
54 | "coordinates=" + coordinates +
55 | ", direction=" + direction +
56 | '}';
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/katas_java/03b-mars-rover-adding-new-feature/src/test/java/MarsRoverTest.java:
--------------------------------------------------------------------------------
1 | import mars_rover.MarsRover;
2 | import org.junit.Test;
3 |
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import static org.hamcrest.core.Is.is;
6 |
7 | public class MarsRoverTest {
8 |
9 | @Test
10 | public void does_nothing_when_receiving_empty_commands_sequence() {
11 | MarsRover marsRover = new MarsRover(0,0, "N");
12 |
13 | marsRover.receive("");
14 |
15 | assertThat(marsRover, is(new MarsRover(0,0, "N")));
16 | }
17 |
18 | @Test
19 | public void turns_right_when_pointing_north() {
20 | MarsRover marsRover = new MarsRover(0,0, "N");
21 |
22 | marsRover.receive("r");
23 |
24 | assertThat(marsRover, is(new MarsRover(0,0, "E")));
25 | }
26 |
27 | @Test
28 | public void turns_right_when_pointing_east() {
29 | MarsRover marsRover = new MarsRover(0,0, "E");
30 |
31 | marsRover.receive("r");
32 |
33 | assertThat(marsRover, is(new MarsRover(0,0, "S")));
34 | }
35 |
36 | @Test
37 | public void turns_right_when_pointing_south() {
38 | MarsRover marsRover = new MarsRover(0,0, "S");
39 |
40 | marsRover.receive("r");
41 |
42 | assertThat(marsRover, is(new MarsRover(0,0, "W")));
43 | }
44 |
45 | @Test
46 | public void turns_right_when_pointing_west() {
47 | MarsRover marsRover = new MarsRover(0,0, "W");
48 |
49 | marsRover.receive("r");
50 |
51 | assertThat(marsRover, is(new MarsRover(0,0, "N")));
52 | }
53 |
54 | @Test
55 | public void turns_left_when_pointing_north() {
56 | MarsRover marsRover = new MarsRover(0,0, "N");
57 |
58 | marsRover.receive("l");
59 |
60 | assertThat(marsRover, is(new MarsRover(0,0, "W")));
61 | }
62 |
63 | @Test
64 | public void turns_left_when_pointing_west() {
65 | MarsRover marsRover = new MarsRover(0,0, "W");
66 |
67 | marsRover.receive("l");
68 |
69 | assertThat(marsRover, is(new MarsRover(0,0, "S")));
70 | }
71 |
72 | @Test
73 | public void turns_left_when_pointing_south() {
74 | MarsRover marsRover = new MarsRover(0,0, "S");
75 |
76 | marsRover.receive("l");
77 |
78 | assertThat(marsRover, is(new MarsRover(0,0, "E")));
79 | }
80 |
81 | @Test
82 | public void turns_left_when_pointing_east() {
83 | MarsRover marsRover = new MarsRover(0,0, "E");
84 |
85 | marsRover.receive("l");
86 |
87 | assertThat(marsRover, is(new MarsRover(0,0, "N")));
88 | }
89 |
90 | @Test
91 | public void moves_forward_when_pointing_north() {
92 | MarsRover marsRover = new MarsRover(5,4, "N");
93 |
94 | marsRover.receive("f");
95 |
96 | assertThat(marsRover, is(new MarsRover(5,5, "N")));
97 | }
98 |
99 | @Test
100 | public void moves_forward_when_pointing_east() {
101 | MarsRover marsRover = new MarsRover(5,4, "E");
102 |
103 | marsRover.receive("f");
104 |
105 | assertThat(marsRover, is(new MarsRover(6,4, "E")));
106 | }
107 |
108 | @Test
109 | public void moves_forward_when_pointing_south() {
110 | MarsRover marsRover = new MarsRover(5,4, "S");
111 |
112 | marsRover.receive("f");
113 |
114 | assertThat(marsRover, is(new MarsRover(5,3, "S")));
115 | }
116 |
117 | @Test
118 | public void moves_forward_when_pointing_west() {
119 | MarsRover marsRover = new MarsRover(5,4, "W");
120 |
121 | marsRover.receive("f");
122 |
123 | assertThat(marsRover, is(new MarsRover(4,4, "W")));
124 | }
125 |
126 | @Test
127 | public void moves_backward_when_pointing_north() {
128 | MarsRover marsRover = new MarsRover(5,4, "N");
129 |
130 | marsRover.receive("b");
131 |
132 | assertThat(marsRover, is(new MarsRover(5,3, "N")));
133 | }
134 |
135 | @Test
136 | public void moves_backward_when_pointing_east() {
137 | MarsRover marsRover = new MarsRover(5,4, "E");
138 |
139 | marsRover.receive("b");
140 |
141 | assertThat(marsRover, is(new MarsRover(4,4, "E")));
142 | }
143 |
144 | @Test
145 | public void moves_backward_when_pointing_south() {
146 | MarsRover marsRover = new MarsRover(5,4, "S");
147 |
148 | marsRover.receive("b");
149 |
150 | assertThat(marsRover, is(new MarsRover(5,5, "S")));
151 | }
152 |
153 | @Test
154 | public void moves_backward_when_pointing_west() {
155 | MarsRover marsRover = new MarsRover(5,4, "W");
156 |
157 | marsRover.receive("b");
158 |
159 | assertThat(marsRover, is(new MarsRover(6,4, "W")));
160 | }
161 |
162 | @Test
163 | public void receives_multiple_commands() {
164 | MarsRover marsRover = new MarsRover(7, 4, "E");
165 |
166 | marsRover.receive("fr");
167 |
168 | assertThat(marsRover, is(new MarsRover(8, 4, "S")));
169 | }
170 |
171 | @Test
172 | public void ignores_unknown_commands() {
173 | MarsRover marsRover = new MarsRover(7, 4, "E");
174 |
175 | marsRover.receive("*");
176 |
177 | assertThat(marsRover, is(new MarsRover(7, 4, "E")));
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/katas_java/04-working-with-code-we-do-not-own/README.md:
--------------------------------------------------------------------------------
1 | ### Brownish Greenfield Gilded Rose Kata*
2 |
3 | Hi and welcome to team Gilded Rose!
4 |
5 | As you know, we are a small inn with a prime location in a prominent city ran by a friendly innkeeper named Allison. We also buy and sell only the finest goods.
6 |
7 | Unfortunately, our goods are constantly degrading in quality as they approach their sell by date. We have a system in place that updates our inventory for us. It was developed by a no-nonsense type named Leeroy, who has moved on to new adventures.
8 |
9 | Your task is to rewrite the system using an old class, that already exists. First an introduction to our system:
10 |
11 | * All items have a SellIn value which denotes the number of days we have to sell the item
12 | * All items have a Quality value which denotes how valuable the item is
13 | * At the end of each day our system lowers both values for every item
14 |
15 | Pretty simple, right? Well this is where it gets interesting:
16 |
17 | * Once the sell by date has passed, Quality degrades twice as fast
18 | * The Quality of an item is never negative
19 | * “Aged Brie” actually increases in Quality the older it gets
20 | * “Sulfuras”, being a legendary item, never has to be sold or decreases in Quality
21 | * The Quality of an item is never more than 50, however “Sulfuras” is a legendary item and as such its Quality is 80 and it never alters.
22 | * “Backstage passes”, like aged brie, increases in Quality as it’s SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concert
23 | * “Conjured” items degrade in Quality twice as fast as normal items
24 |
25 | Your task is to create the system from scratch, with only a restriction:
26 | **You must use the Item class that Leeroy developed**.
27 |
28 | But beware, before leaving Leeroy casted a curse on this class, so:
29 |
30 | 1. It must be used.
31 | 2. It can not be modified. This prohibition applies to modifying it, deleting it, adding new methods, deleting methodts, changing visibility of stuff, etc.
32 | 3. It cannot be extended.
33 |
34 | If you break any of these rules a goblin in the corner will insta-rage and destroy you.
35 |
36 | You'll find the initial code for this kata in this repository.
37 |
38 | Good luck and beware the goblin!
39 |
40 | *Adapted from the [Brownish Greenfield Gilded Rose Kata](https://alvarogarcia7.github.io/blog/2016/04/18/brownish-greenfield-gilded-rose-kata-formulation/)
--------------------------------------------------------------------------------
/katas_java/04-working-with-code-we-do-not-own/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | working-with-code-we-do-not-own
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/katas_java/04-working-with-code-we-do-not-own/src/main/java/items/Item.java:
--------------------------------------------------------------------------------
1 | package items;
2 |
3 | public class Item {
4 |
5 | public String name;
6 |
7 | public int sellIn;
8 |
9 | public int quality;
10 |
11 | public Item(String name, int sellIn, int quality) {
12 | this.name = name;
13 | this.sellIn = sellIn;
14 | this.quality = quality;
15 | }
16 |
17 | @Override
18 | public String toString() {
19 | return this.name + ", " + this.sellIn + ", " + this.quality;
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/katas_java/04-working-with-code-we-do-not-own/src/test/java/GildedRoseTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Test;
2 |
3 | import static org.hamcrest.MatcherAssert.assertThat;
4 | import static org.hamcrest.Matchers.is;
5 |
6 | public class GildedRoseTest {
7 | @Test
8 | public void shouldFail() {
9 | assertThat(false, is(true));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/README.md:
--------------------------------------------------------------------------------
1 | ## Goal
2 | This code computes the prices of the beverages served in our coffe house.
3 |
4 | We'd like you to add an optional cinnamon supplement that costs 0.05€
5 | to all our existing catalog of beverages.
6 |
7 | ### Tips:
8 | You might want to refactor the code a bit first.
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | katas-java
4 | beverages_prices
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 |
18 |
19 |
20 | junit
21 | junit
22 | 4.13.1
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Beverage.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public interface Beverage {
4 | double price();
5 | }
6 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Coffee.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class Coffee implements Beverage {
4 | @Override
5 | public double price() {
6 | return 1.2;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/CoffeeWithMilk.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class CoffeeWithMilk extends Coffee {
4 | @Override
5 | public double price() {
6 | return super.price() + 0.10;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/CoffeeWithMilkAndCream.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class CoffeeWithMilkAndCream extends Coffee {
4 | @Override
5 | public double price() {
6 | return super.price() + 0.25;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/HotChocolate.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class HotChocolate implements Beverage {
4 | @Override
5 | public double price() {
6 | return 1.45;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/HotChocolateWithCream.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class HotChocolateWithCream extends HotChocolate {
4 | @Override
5 | public double price() {
6 | return 1.45 + 0.15;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/Tea.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class Tea implements Beverage {
4 | @Override
5 | public double price() {
6 | return 1.5;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/main/java/beverages/TeaWithMilk.java:
--------------------------------------------------------------------------------
1 | package beverages;
2 |
3 | public class TeaWithMilk extends Tea {
4 | @Override
5 | public double price() {
6 | return super.price() + 0.10;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/katas_java/05-refactoring-awful-inheritance-use-with-beverage-prices-kata/src/test/java/unit_tests/BeveragesPricingTest.java:
--------------------------------------------------------------------------------
1 | package unit_tests;
2 |
3 | import beverages.*;
4 | import org.junit.Test;
5 |
6 | import static org.hamcrest.CoreMatchers.is;
7 | import static org.hamcrest.MatcherAssert.assertThat;
8 | import static org.hamcrest.Matchers.closeTo;
9 |
10 | public class BeveragesPricingTest {
11 | @Test
12 | public void computes_coffee_price() {
13 | Beverage coffee = new Coffee();
14 | assertThat(coffee.price(), is(closeTo(1.20, 0.001)));
15 | }
16 |
17 | @Test
18 | public void computes_tea_price() {
19 | Beverage tea = new Tea();
20 | assertThat(tea.price(), is(closeTo(1.50, 0.001)));
21 | }
22 |
23 | @Test
24 | public void computes_hot_chocolate_price() {
25 | Beverage hotChocolate = new HotChocolate();
26 | assertThat(hotChocolate.price(), is(closeTo(1.45, 0.001)));
27 | }
28 |
29 | @Test
30 | public void computes_tea_with_milk_price() {
31 | Tea teaWithMilk = new TeaWithMilk();
32 | assertThat(teaWithMilk.price(), is(closeTo(1.60, 0.001)));
33 | }
34 |
35 | @Test
36 | public void computes_coffee_with_milk_price() {
37 | Coffee coffeeWithMilk = new CoffeeWithMilk();
38 | assertThat(coffeeWithMilk.price(), is(closeTo(1.30, 0.001)));
39 | }
40 |
41 | @Test
42 | public void computes_coffee_with_milk_and_cream_price() {
43 | Coffee coffeeWithMilkAndCream = new CoffeeWithMilkAndCream();
44 | assertThat(coffeeWithMilkAndCream.price(), is(closeTo(1.45, 0.001)));
45 | }
46 |
47 | @Test
48 | public void computes_hot_chocolate_with_cream_price() {
49 | HotChocolateWithCream hotChocolateWithCream = new HotChocolateWithCream();
50 | assertThat(hotChocolateWithCream.price(), is(closeTo(1.60, 0.001)));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | refactgoring-to-hexagonal-architecture
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 | jar
19 | test
20 |
21 |
22 |
23 | junit
24 | junit
25 | 4.13.1
26 | jar
27 | test
28 |
29 |
30 |
31 | org.mockito
32 | mockito-all
33 | 1.10.8
34 | test
35 |
36 |
37 |
38 | com.sun.mail
39 | javax.mail
40 | 1.5.5
41 |
42 |
43 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/readme.md:
--------------------------------------------------------------------------------
1 | # Birthday Greetings Kata
2 |
3 | We'll refactor a very convoluted service to segregate its business logic from its infrastructure logic.
4 |
5 | We'll use an architectural style called [hexagonal architecture or ports and adapters](http://alistair.cockburn.us/Hexagonal+architecture).
6 |
7 | To practice we'll do [Matteo Vaccari](http://matteo.vaccari.name/blog/)'s Birthday Greetings Refactoring Kata.
8 |
9 | To learn more about this great kata, read Matteo's post about it on its blog: [The birthday greetings kata](http://matteo.vaccari.name/blog/archives/154). That post also constains a great explanation of the hexagonal architecture.
10 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/BirthdayService.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 | import java.text.ParseException;
7 |
8 | import javax.mail.Message;
9 | import javax.mail.MessagingException;
10 | import javax.mail.Session;
11 | import javax.mail.Transport;
12 | import javax.mail.internet.AddressException;
13 | import javax.mail.internet.InternetAddress;
14 | import javax.mail.internet.MimeMessage;
15 |
16 | public class BirthdayService {
17 |
18 | public void sendGreetings(String fileName, OurDate ourDate,
19 | String smtpHost, int smtpPort) throws IOException, ParseException,
20 | AddressException, MessagingException {
21 | BufferedReader in = new BufferedReader(new FileReader(fileName));
22 | String str = "";
23 | str = in.readLine(); // skip header
24 | while ((str = in.readLine()) != null) {
25 | String[] employeeData = str.split(", ");
26 | Employee employee = new Employee(employeeData[1], employeeData[0],
27 | employeeData[2], employeeData[3]);
28 | if (employee.isBirthday(ourDate)) {
29 | String recipient = employee.getEmail();
30 | String body = "Happy Birthday, dear %NAME%!".replace("%NAME%",
31 | employee.getFirstName());
32 | String subject = "Happy Birthday!";
33 | sendMessage(smtpHost, smtpPort, "sender@here.com", subject,
34 | body, recipient);
35 | }
36 | }
37 | }
38 |
39 | private void sendMessage(String smtpHost, int smtpPort, String sender,
40 | String subject, String body, String recipient)
41 | throws AddressException, MessagingException {
42 | // Create a mail session
43 | java.util.Properties props = new java.util.Properties();
44 | props.put("mail.smtp.host", smtpHost);
45 | props.put("mail.smtp.port", "" + smtpPort);
46 | Session session = Session.getDefaultInstance(props, null);
47 |
48 | // Construct the message
49 | Message msg = new MimeMessage(session);
50 | msg.setFrom(new InternetAddress(sender));
51 | msg.setRecipient(Message.RecipientType.TO, new InternetAddress(
52 | recipient));
53 | msg.setSubject(subject);
54 | msg.setText(body);
55 |
56 | // Send the message
57 | sendMessage(msg);
58 | }
59 |
60 | // made protected for testing :-(
61 | protected void sendMessage(Message msg) throws MessagingException {
62 | Transport.send(msg);
63 | }
64 |
65 | public static void main(String[] args) {
66 | BirthdayService service = new BirthdayService();
67 | try {
68 | service.sendGreetings("employee_data.txt",
69 | new OurDate("2008/10/08"), "localhost", 25);
70 | } catch (Exception e) {
71 | e.printStackTrace();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/Employee.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings;
2 |
3 | import java.text.ParseException;
4 |
5 | public class Employee {
6 |
7 | private OurDate birthDate;
8 | private String lastName;
9 | private String firstName;
10 | private String email;
11 |
12 | public Employee(String firstName, String lastName, String birthDate,
13 | String email) throws ParseException {
14 | this.firstName = firstName;
15 | this.lastName = lastName;
16 | this.birthDate = new OurDate(birthDate);
17 | this.email = email;
18 | }
19 |
20 | public boolean isBirthday(OurDate today) {
21 | return today.isSameDay(birthDate);
22 | }
23 |
24 | public String getEmail() {
25 | return email;
26 | }
27 |
28 | public String getFirstName() {
29 | return firstName;
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return "Employee " + firstName + " " + lastName + " <" + email
35 | + "> born " + birthDate;
36 | }
37 |
38 | @Override
39 | public int hashCode() {
40 | final int prime = 31;
41 | int result = 1;
42 | result = prime * result
43 | + ((birthDate == null) ? 0 : birthDate.hashCode());
44 | result = prime * result + ((email == null) ? 0 : email.hashCode());
45 | result = prime * result
46 | + ((firstName == null) ? 0 : firstName.hashCode());
47 | result = prime * result
48 | + ((lastName == null) ? 0 : lastName.hashCode());
49 | return result;
50 | }
51 |
52 | @Override
53 | public boolean equals(Object obj) {
54 | if (this == obj)
55 | return true;
56 | if (obj == null)
57 | return false;
58 | if (!(obj instanceof Employee))
59 | return false;
60 | Employee other = (Employee) obj;
61 | if (birthDate == null) {
62 | if (other.birthDate != null)
63 | return false;
64 | } else if (!birthDate.equals(other.birthDate))
65 | return false;
66 | if (email == null) {
67 | if (other.email != null)
68 | return false;
69 | } else if (!email.equals(other.email))
70 | return false;
71 | if (firstName == null) {
72 | if (other.firstName != null)
73 | return false;
74 | } else if (!firstName.equals(other.firstName))
75 | return false;
76 | if (lastName == null) {
77 | if (other.lastName != null)
78 | return false;
79 | } else if (!lastName.equals(other.lastName))
80 | return false;
81 | return true;
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/main/java/birthdaygreetings/OurDate.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings;
2 |
3 | import java.text.ParseException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Date;
6 | import java.util.GregorianCalendar;
7 |
8 | public class OurDate {
9 |
10 | private Date date;
11 |
12 | public OurDate(String yyyyMMdd) throws ParseException {
13 | date = new SimpleDateFormat("yyyy/MM/dd").parse(yyyyMMdd);
14 | }
15 |
16 | public int getDay() {
17 | return getPartOfDate(GregorianCalendar.DAY_OF_MONTH);
18 | }
19 |
20 | public int getMonth() {
21 | return 1 + getPartOfDate(GregorianCalendar.MONTH);
22 | }
23 |
24 | public boolean isSameDay(OurDate anotherDate) {
25 | return anotherDate.getDay() == this.getDay()
26 | && anotherDate.getMonth() == this.getMonth();
27 | }
28 |
29 | @Override
30 | public int hashCode() {
31 | return date.hashCode();
32 | }
33 |
34 | @Override
35 | public boolean equals(Object obj) {
36 | if (!(obj instanceof OurDate))
37 | return false;
38 | OurDate other = (OurDate) obj;
39 | return other.date.equals(this.date);
40 | }
41 |
42 | private int getPartOfDate(int part) {
43 | GregorianCalendar calendar = new GregorianCalendar();
44 | calendar.setTime(date);
45 | return calendar.get(part);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/AcceptanceTest.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.test;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import javax.mail.Message;
7 | import javax.mail.MessagingException;
8 |
9 | import birthdaygreetings.BirthdayService;
10 | import birthdaygreetings.OurDate;
11 |
12 | import org.junit.Before;
13 | import org.junit.Test;
14 |
15 | import static org.junit.Assert.*;
16 |
17 | public class AcceptanceTest {
18 |
19 | private static final int SMTP_PORT = 25;
20 | private List messagesSent;
21 | private BirthdayService service;
22 |
23 | @Before
24 | public void setUp() throws Exception {
25 | messagesSent = new ArrayList();
26 |
27 | service = new BirthdayService() {
28 | @Override
29 | protected void sendMessage(Message msg) throws MessagingException {
30 | messagesSent.add(msg);
31 | }
32 | };
33 | }
34 |
35 | @Test
36 | public void baseScenario() throws Exception {
37 |
38 | service.sendGreetings("src/test/resources/employee_data.txt",
39 | new OurDate("2008/10/08"), "localhost", SMTP_PORT);
40 |
41 | assertEquals("message not sent?", 1, messagesSent.size());
42 | Message message = messagesSent.get(0);
43 | assertEquals("Happy Birthday, dear John!", message.getContent());
44 | assertEquals("Happy Birthday!", message.getSubject());
45 | assertEquals(1, message.getAllRecipients().length);
46 | assertEquals("john.doe@foobar.com",
47 | message.getAllRecipients()[0].toString());
48 | }
49 |
50 | @Test
51 | public void willNotSendEmailsWhenNobodysBirthday() throws Exception {
52 | service.sendGreetings("src/test/resources/employee_data.txt",
53 | new OurDate("2008/01/01"), "localhost", SMTP_PORT);
54 |
55 | assertEquals("what? messages?", 0, messagesSent.size());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/EmployeeTest.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.test;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import birthdaygreetings.Employee;
6 | import birthdaygreetings.OurDate;
7 |
8 | import org.junit.Test;
9 |
10 | public class EmployeeTest {
11 |
12 | @Test
13 | public void testBirthday() throws Exception {
14 | Employee employee = new Employee("foo", "bar", "1990/01/31", "a@b.c");
15 | assertFalse("not his birthday",
16 | employee.isBirthday(new OurDate("2008/01/30")));
17 | assertTrue("his birthday",
18 | employee.isBirthday(new OurDate("2008/01/31")));
19 | }
20 |
21 | @Test
22 | public void equality() throws Exception {
23 | Employee base = new Employee("First", "Last", "1999/09/01",
24 | "first@last.com");
25 | Employee same = new Employee("First", "Last", "1999/09/01",
26 | "first@last.com");
27 | Employee different = new Employee("First", "Last", "1999/09/01",
28 | "boom@boom.com");
29 |
30 | assertFalse(base.equals(null));
31 | assertFalse(base.equals(""));
32 | assertTrue(base.equals(same));
33 | assertFalse(base.equals(different));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/test/java/birthdaygreetings/test/OurDateTest.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.test;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import birthdaygreetings.OurDate;
6 |
7 | import org.junit.Test;
8 |
9 | public class OurDateTest {
10 | @Test
11 | public void getters() throws Exception {
12 | OurDate ourDate = new OurDate("1789/01/24");
13 | assertEquals(1, ourDate.getMonth());
14 | assertEquals(24, ourDate.getDay());
15 | }
16 |
17 | @Test
18 | public void isSameDate() throws Exception {
19 | OurDate ourDate = new OurDate("1789/01/24");
20 | OurDate sameDay = new OurDate("2001/01/24");
21 | OurDate notSameDay = new OurDate("1789/01/25");
22 | OurDate notSameMonth = new OurDate("1789/02/25");
23 |
24 | assertTrue("same", ourDate.isSameDay(sameDay));
25 | assertFalse("not same day", ourDate.isSameDay(notSameDay));
26 | assertFalse("not same month", ourDate.isSameDay(notSameMonth));
27 | }
28 |
29 | @Test
30 | public void equality() throws Exception {
31 | OurDate base = new OurDate("2000/01/02");
32 | OurDate same = new OurDate("2000/01/02");
33 | OurDate different = new OurDate("2000/01/04");
34 |
35 | assertFalse(base.equals(null));
36 | assertFalse(base.equals(""));
37 | assertTrue(base.equals(base));
38 | assertTrue(base.equals(same));
39 | assertFalse(base.equals(different));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/katas_java/06-refactoring-to-hexagonal-architecture/src/test/resources/employee_data.txt:
--------------------------------------------------------------------------------
1 | last_name, first_name, date_of_birth, email
2 | Doe, John, 1982/10/08, john.doe@foobar.com
3 | Ann, Mary, 1975/03/11, mary.ann@foobar.com
4 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.codesai.katas-java
4 | refactgoring-to-hexagonal-architecture-2
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 | jar
18 | test
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.13.1
25 | jar
26 | test
27 |
28 |
29 |
30 | org.mockito
31 | mockito-all
32 | 1.10.8
33 | test
34 |
35 |
36 |
37 | com.sun.mail
38 | javax.mail
39 | 1.5.5
40 |
41 |
42 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/readme.md:
--------------------------------------------------------------------------------
1 | # Birthday Greetings Kata 2
2 |
3 | We'll continue refactoring a now less convoluted service to extract the remaining infrastructure logic.
4 |
5 | We'll use an architectural style called [hexagonal architecture or ports and adapters](http://alistair.cockburn.us/Hexagonal+architecture).
6 |
7 | To practice we'll do [Matteo Vaccari](http://matteo.vaccari.name/blog/)'s Birthday Greetings Refactoring Kata.
8 |
9 | To learn more about this great kata, read Matteo's post about it on its blog: [The birthday greetings kata](http://matteo.vaccari.name/blog/archives/154). That post also constains a great explanation of the hexagonal architecture.
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/application/BirthdayService.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.application;
2 |
3 | import birthdaygreetings.core.Employee;
4 | import birthdaygreetings.core.EmployeesRepository;
5 | import birthdaygreetings.core.GreetingMessage;
6 | import birthdaygreetings.core.OurDate;
7 |
8 | import javax.mail.Message;
9 | import javax.mail.MessagingException;
10 | import javax.mail.Session;
11 | import javax.mail.Transport;
12 | import javax.mail.internet.InternetAddress;
13 | import javax.mail.internet.MimeMessage;
14 | import java.util.List;
15 |
16 | public class BirthdayService {
17 |
18 | private EmployeesRepository employeesRepository;
19 |
20 | public BirthdayService(EmployeesRepository employeesRepository) {
21 | this.employeesRepository = employeesRepository;
22 | }
23 |
24 | public void sendGreetings(OurDate date, String smtpHost, int smtpPort, String sender) throws MessagingException {
25 |
26 | send(greetingMessagesFor(employeesHavingBirthday(date)),
27 | smtpHost, smtpPort, sender);
28 | }
29 |
30 | private List greetingMessagesFor(List employees) {
31 | return GreetingMessage.generateForSome(employees);
32 | }
33 |
34 | private List employeesHavingBirthday(OurDate today) {
35 | return employeesRepository.whoseBirthdayIs(today);
36 | }
37 |
38 | private void send(List messages, String smtpHost, int smtpPort, String sender) throws MessagingException {
39 | for (GreetingMessage message : messages) {
40 | String recipient = message.to();
41 | String body = message.text();
42 | String subject = message.subject();
43 | sendMessage(smtpHost, smtpPort, sender, subject, body, recipient);
44 | }
45 | }
46 |
47 | private void sendMessage(String smtpHost, int smtpPort, String sender,
48 | String subject, String body, String recipient)
49 | throws MessagingException {
50 | // Create a mail session
51 | java.util.Properties props = new java.util.Properties();
52 | props.put("mail.smtp.host", smtpHost);
53 | props.put("mail.smtp.port", "" + smtpPort);
54 | Session session = Session.getDefaultInstance(props, null);
55 |
56 | // Construct the message
57 | Message msg = new MimeMessage(session);
58 | msg.setFrom(new InternetAddress(sender));
59 | msg.setRecipient(Message.RecipientType.TO, new InternetAddress(
60 | recipient));
61 | msg.setSubject(subject);
62 | msg.setText(body);
63 |
64 | // Send the message
65 | sendMessage(msg);
66 | }
67 |
68 | // made protected for testing :-(
69 | protected void sendMessage(Message msg) throws MessagingException {
70 | Transport.send(msg);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/application/Main.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.application;
2 |
3 | import birthdaygreetings.core.OurDate;
4 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository;
5 |
6 | import java.util.Date;
7 |
8 | public class Main {
9 |
10 | private static final String EMPLOYEES_FILE_PATH = "employee_data.txt";
11 | private static final String SENDER_EMAIL_ADDRESS = "sender@here.com";
12 | private static final String HOST = "localhost";
13 | private static final int SMTP_PORT = 25;
14 |
15 | public static void main(String[] args) {
16 | BirthdayService service = new BirthdayService(
17 | new FileEmployeesRepository(EMPLOYEES_FILE_PATH));
18 | try {
19 | OurDate today = new OurDate(new Date());
20 | service.sendGreetings(today, HOST, SMTP_PORT, SENDER_EMAIL_ADDRESS);
21 | } catch (Exception e) {
22 | e.printStackTrace();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/CannotReadEmployeesException.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | public class CannotReadEmployeesException extends RuntimeException {
4 | public CannotReadEmployeesException(String cause, Exception exception) {
5 | super(cause, exception);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/Employee.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | public class Employee {
4 |
5 | private OurDate birthDate;
6 | private String lastName;
7 | private String firstName;
8 | private String email;
9 |
10 | public Employee(String firstName, String lastName, OurDate birthDate,
11 | String email) {
12 | this.firstName = firstName;
13 | this.lastName = lastName;
14 | this.birthDate = birthDate;
15 | this.email = email;
16 | }
17 |
18 | public boolean isBirthday(OurDate today) {
19 | return today.isSameDay(birthDate);
20 | }
21 |
22 | String email() {
23 | return email;
24 | }
25 |
26 | String firstName() {
27 | return firstName;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Employee " + firstName + " " + lastName + " <" + email
33 | + "> born " + birthDate;
34 | }
35 |
36 | @Override
37 | public boolean equals(Object o) {
38 | if (this == o) return true;
39 | if (!(o instanceof Employee)) return false;
40 |
41 | Employee employee = (Employee) o;
42 |
43 | if (birthDate != null ? !birthDate.equals(employee.birthDate) : employee.birthDate != null) return false;
44 | if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false;
45 | if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false;
46 | return email != null ? email.equals(employee.email) : employee.email == null;
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | int result = birthDate != null ? birthDate.hashCode() : 0;
52 | result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
53 | result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
54 | result = 31 * result + (email != null ? email.hashCode() : 0);
55 | return result;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/EmployeesRepository.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | import java.util.List;
4 |
5 | public interface EmployeesRepository {
6 | List whoseBirthdayIs(OurDate today);
7 | }
8 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/Greeting.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | class Greeting {
4 | private final String header;
5 | private final String content;
6 |
7 | private Greeting(String header, String content) {
8 | this.header = header;
9 | this.content = content;
10 | }
11 |
12 | static Greeting forBirthdayOf(Employee employee){
13 | String content = String.format("Happy Birthday, dear %s!", employee.firstName());
14 | String header = "Happy Birthday!";
15 | return new Greeting(header, content);
16 | }
17 |
18 | String header() {
19 | return header;
20 | }
21 |
22 | String content() {
23 | return content;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/GreetingMessage.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | import java.util.List;
4 |
5 | import static java.util.stream.Collectors.toList;
6 |
7 | public class GreetingMessage {
8 |
9 | private final String to;
10 | private final Greeting greeting;
11 |
12 | private GreetingMessage(String to, Greeting greeting) {
13 | this.to = to;
14 | this.greeting = greeting;
15 | }
16 |
17 | public static List generateForSome(List employees) {
18 | return employees.stream().map(GreetingMessage::generateFor).collect(toList());
19 | }
20 |
21 | private static GreetingMessage generateFor(Employee employee) {
22 | Greeting greeting = Greeting.forBirthdayOf(employee);
23 | String recipient = employee.email();
24 | return new GreetingMessage(recipient, greeting);
25 | }
26 |
27 | public String subject() {
28 | return this.greeting.header();
29 | }
30 |
31 | public String text() {
32 | return this.greeting.content();
33 | }
34 |
35 | public String to() {
36 | return this.to;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/core/OurDate.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.core;
2 |
3 | import java.util.Date;
4 | import java.util.GregorianCalendar;
5 |
6 | public class OurDate {
7 | private Date date;
8 |
9 | public OurDate(Date date) {
10 | this.date = date;
11 | }
12 |
13 | public boolean isSameDay(OurDate anotherDate) {
14 | return anotherDate.getDay() == this.getDay()
15 | && anotherDate.getMonth() == this.getMonth();
16 | }
17 |
18 | @Override
19 | public boolean equals(Object o) {
20 | if (this == o) return true;
21 | if (!(o instanceof OurDate)) return false;
22 |
23 | OurDate ourDate = (OurDate) o;
24 |
25 | return date != null ? date.equals(ourDate.date) : ourDate.date == null;
26 | }
27 |
28 | @Override
29 | public int hashCode() {
30 | return date != null ? date.hashCode() : 0;
31 | }
32 |
33 | private int getDay() {
34 | return getPartOfDate(GregorianCalendar.DAY_OF_MONTH);
35 | }
36 |
37 | private int getMonth() {
38 | return 1 + getPartOfDate(GregorianCalendar.MONTH);
39 | }
40 |
41 | private int getPartOfDate(int part) {
42 | GregorianCalendar calendar = new GregorianCalendar();
43 | calendar.setTime(date);
44 | return calendar.get(part);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/DateRepresentation.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.infrastructure.repositories;
2 |
3 | import birthdaygreetings.core.OurDate;
4 |
5 | import java.text.ParseException;
6 | import java.text.SimpleDateFormat;
7 |
8 | public class DateRepresentation {
9 | private static final String DATE_FORMAT = "yyyy/MM/dd";
10 | private final String dateAsString;
11 |
12 | public DateRepresentation(String dateAsString) {
13 | this.dateAsString = dateAsString;
14 | }
15 |
16 | public OurDate toDate() throws ParseException {
17 | return new OurDate(
18 | new SimpleDateFormat(DATE_FORMAT).parse(dateAsString)
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/EmployeesFile.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.infrastructure.repositories;
2 |
3 | import birthdaygreetings.core.CannotReadEmployeesException;
4 | import birthdaygreetings.core.Employee;
5 | import birthdaygreetings.core.OurDate;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.nio.file.Path;
10 | import java.nio.file.Paths;
11 | import java.text.ParseException;
12 | import java.util.ArrayList;
13 | import java.util.Iterator;
14 | import java.util.List;
15 |
16 | class EmployeesFile {
17 | private final Iterator linesIterator;
18 |
19 | private EmployeesFile(Iterator linesIterator) {
20 | this.linesIterator = linesIterator;
21 | }
22 |
23 | public static EmployeesFile loadFrom(String path) {
24 | return new EmployeesFile(FileReader.readSkippingHeader(path));
25 | }
26 |
27 | public List extractEmployees() {
28 | List employees = new ArrayList<>();
29 | while (linesIterator.hasNext()) {
30 | String line = linesIterator.next();
31 | EmployeeCsvRepresentation representation = new EmployeeCsvRepresentation(line);
32 | employees.add(representation.convertToEmployee());
33 | }
34 | return employees;
35 | }
36 |
37 | private class EmployeeCsvRepresentation {
38 | private String content;
39 | private final String[] tokens;
40 |
41 | public EmployeeCsvRepresentation(String content) {
42 | this.content = content;
43 | this.tokens = content.split(", ");
44 | }
45 |
46 | public Employee convertToEmployee() {
47 | return new Employee(firstName(), lastName(), birthDate(), email());
48 | }
49 |
50 | private String lastName() {
51 | return tokens[0];
52 | }
53 |
54 | private String email() {
55 | return tokens[3];
56 | }
57 |
58 | private String firstName() {
59 | return tokens[1];
60 | }
61 |
62 | private OurDate birthDate() {
63 | try {
64 | return new DateRepresentation(dateAsString()).toDate();
65 | } catch (ParseException exception) {
66 | throw new CannotReadEmployeesException(
67 | String.format("Badly formatted employee birth date in: '%s'", content),
68 | exception
69 | );
70 | }
71 | }
72 |
73 | private String dateAsString() {
74 | return tokens[2];
75 | }
76 | }
77 |
78 | private static class FileReader {
79 | public static Iterator readSkippingHeader(String pathString) {
80 | Path path = Paths.get(pathString);
81 | try {
82 | return skipHeader(readFile(path));
83 | } catch (IOException exception) {
84 | throw new CannotReadEmployeesException(
85 | String.format("cannot loadFrom file = '%s'", path.toAbsolutePath()),
86 | exception
87 | );
88 | }
89 | }
90 |
91 | private static Iterator readFile(Path path) throws IOException {
92 | List lines = Files.readAllLines(path);
93 | return lines.iterator();
94 | }
95 |
96 | private static Iterator skipHeader(Iterator iterator) {
97 | iterator.next();
98 | return iterator;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/main/java/birthdaygreetings/infrastructure/repositories/FileEmployeesRepository.java:
--------------------------------------------------------------------------------
1 | package birthdaygreetings.infrastructure.repositories;
2 |
3 | import birthdaygreetings.core.Employee;
4 | import birthdaygreetings.core.EmployeesRepository;
5 | import birthdaygreetings.core.OurDate;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | public class FileEmployeesRepository implements EmployeesRepository {
11 | private final String path;
12 |
13 | public FileEmployeesRepository(String path) {
14 | this.path = path;
15 | }
16 |
17 | @Override
18 | public List whoseBirthdayIs(OurDate today) {
19 | return allEmployees().stream()
20 | .filter(employee -> employee.isBirthday(today))
21 | .collect(Collectors.toList());
22 | }
23 |
24 | private List allEmployees() {
25 | EmployeesFile employeesFile = EmployeesFile.loadFrom(path);
26 | return employeesFile.extractEmployees();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/application/BirthdayServiceAcceptanceTest.java:
--------------------------------------------------------------------------------
1 | package test.birthdaygreetings.application;
2 |
3 | import birthdaygreetings.application.BirthdayService;
4 | import birthdaygreetings.core.OurDate;
5 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import javax.mail.Message;
10 | import javax.mail.MessagingException;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import static org.junit.Assert.assertEquals;
15 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString;
16 |
17 | public class BirthdayServiceAcceptanceTest {
18 |
19 | private static final int SMTP_PORT = 25;
20 | private String SMTP_HOST = "localhost";
21 | private static final String FROM = "sender@here.com";
22 | private List messagesSent;
23 | private BirthdayService service;
24 | private static final String EMPLOYEES_FILE_PATH = "src/test/resources/employee_data.txt";
25 |
26 | @Before
27 | public void setUp() throws Exception {
28 | messagesSent = new ArrayList<>();
29 |
30 | service = new BirthdayService(new FileEmployeesRepository(EMPLOYEES_FILE_PATH)) {
31 | @Override
32 | protected void sendMessage(Message msg) throws MessagingException {
33 | messagesSent.add(msg);
34 | }
35 | };
36 | }
37 |
38 | @Test
39 | public void baseScenario() throws Exception {
40 | OurDate today = ourDateFromString("2008/10/08");
41 |
42 | service.sendGreetings(today, SMTP_HOST, SMTP_PORT, FROM);
43 |
44 | assertEquals("message not sent?", 1, messagesSent.size());
45 | Message message = messagesSent.get(0);
46 | assertEquals("Happy Birthday, dear John!", message.getContent());
47 | assertEquals("Happy Birthday!", message.getSubject());
48 | assertEquals(1, message.getAllRecipients().length);
49 | assertEquals("john.doe@foobar.com", message.getAllRecipients()[0].toString());
50 | }
51 |
52 | @Test
53 | public void willNotSendEmailsWhenNobodysBirthday() throws Exception {
54 | OurDate today = ourDateFromString("2008/01/01");
55 |
56 | service.sendGreetings(today, SMTP_HOST, SMTP_PORT, FROM);
57 |
58 | assertEquals(0, messagesSent.size());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/core/EmployeeTest.java:
--------------------------------------------------------------------------------
1 | package test.birthdaygreetings.core;
2 |
3 | import birthdaygreetings.core.Employee;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertFalse;
7 | import static org.junit.Assert.assertTrue;
8 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString;
9 |
10 | public class EmployeeTest {
11 |
12 | @Test
13 | public void testBirthday() throws Exception {
14 | Employee employee = new Employee("foo", "bar", ourDateFromString("1990/01/31"), "a@b.c");
15 |
16 | assertFalse("no birthday", employee.isBirthday(ourDateFromString("2008/01/30")));
17 |
18 | assertTrue("birthday", employee.isBirthday(ourDateFromString("2008/01/31")));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/core/OurDateTest.java:
--------------------------------------------------------------------------------
1 | package test.birthdaygreetings.core;
2 |
3 | import birthdaygreetings.core.OurDate;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertFalse;
7 | import static org.junit.Assert.assertTrue;
8 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString;
9 |
10 | public class OurDateTest {
11 | @Test
12 | public void identifies_if_two_dates_were_in_the_same_day() throws Exception {
13 | OurDate ourDate = ourDateFromString("1789/01/24");
14 | OurDate sameDay = ourDateFromString("2001/01/24");
15 | OurDate notSameDay = ourDateFromString("1789/01/25");
16 | OurDate notSameMonth = ourDateFromString("1789/02/25");
17 |
18 | assertTrue("same", ourDate.isSameDay(sameDay));
19 | assertFalse("not same day", ourDate.isSameDay(notSameDay));
20 | assertFalse("not same month", ourDate.isSameDay(notSameMonth));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/helpers/OurDateFactory.java:
--------------------------------------------------------------------------------
1 | package test.birthdaygreetings.helpers;
2 |
3 | import birthdaygreetings.core.OurDate;
4 | import birthdaygreetings.infrastructure.repositories.DateRepresentation;
5 |
6 | import java.text.ParseException;
7 |
8 | public class OurDateFactory {
9 | public static OurDate ourDateFromString(String dateAsString) throws ParseException {
10 | return new DateRepresentation(dateAsString).toDate();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/java/test/birthdaygreetings/infrastructure/repositories/FileEmployeeRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package test.birthdaygreetings.infrastructure.repositories;
2 |
3 | import birthdaygreetings.core.CannotReadEmployeesException;
4 | import birthdaygreetings.core.EmployeesRepository;
5 | import birthdaygreetings.core.OurDate;
6 | import birthdaygreetings.infrastructure.repositories.FileEmployeesRepository;
7 | import org.junit.Before;
8 | import org.junit.Rule;
9 | import org.junit.Test;
10 | import org.junit.rules.ExpectedException;
11 |
12 | import static org.hamcrest.core.StringContains.containsString;
13 | import static test.birthdaygreetings.helpers.OurDateFactory.ourDateFromString;
14 |
15 | public class FileEmployeeRepositoryTest {
16 |
17 | @Rule
18 | public ExpectedException expected = ExpectedException.none();
19 | private OurDate ANY_DATE;
20 |
21 | @Before
22 | public void setUp() throws Exception {
23 | ANY_DATE = ourDateFromString("2016/01/01");
24 | }
25 |
26 | @Test
27 | public void fails_when_the_file_does_not_exist() throws Exception {
28 |
29 | EmployeesRepository employeesRepository = new FileEmployeesRepository("non-existing.file");
30 | expected.expect(CannotReadEmployeesException.class);
31 | expected.expectMessage(containsString("cannot loadFrom file"));
32 | expected.expectMessage(containsString("non-existing.file"));
33 |
34 | employeesRepository.whoseBirthdayIs(ANY_DATE);
35 | }
36 |
37 | @Test
38 | public void fails_when_the_file_does_not_have_the_necessary_fields() throws Exception {
39 |
40 | EmployeesRepository employeesRepository = new FileEmployeesRepository("src/test/resources/wrong_data__wrong-date-format.csv");
41 | expected.expect(CannotReadEmployeesException.class);
42 | expected.expectMessage(containsString("Badly formatted employee birth date in"));
43 |
44 | employeesRepository.whoseBirthdayIs(ANY_DATE);
45 | }
46 | }
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/employee_data.txt:
--------------------------------------------------------------------------------
1 | last_name, first_name, date_of_birth, email
2 | Doe, John, 1982/10/08, john.doe@foobar.com
3 | Ann, Mary, 1975/03/11, mary.ann@foobar.com
4 |
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/wrong_data__one-field-only.csv:
--------------------------------------------------------------------------------
1 | #this is missing two fields
2 | 1,
--------------------------------------------------------------------------------
/katas_java/07-refactoring-to-hexagonal-architecture-2/src/test/resources/wrong_data__wrong-date-format.csv:
--------------------------------------------------------------------------------
1 | #the date format is wrong
2 | Doe, John, 2016-01-01, john.doe@foobar.com
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/README.md:
--------------------------------------------------------------------------------
1 | ### TextConverter exercise
2 |
3 | The `HtmlTextConverter` class is designed to reformat a plain text file for display in a browser.
4 |
5 | We'll use TDD to add any new behavior to this code.
6 |
7 | Try to do the minimum changes possible to the code.
8 |
9 | #### First iteration
10 | Modify the code so that it escapes `'` and `"` substituting them by `"`.
11 |
12 | 1. Have a look at the code and identify the **change points** (the things that need to be modified
13 | to add the new behavior).
14 |
15 | 2. Write unit tests to protect the existing behavior to avoid introducing regressions when you change the code.
16 |
17 | a. First you'll have to find some **test points** (places around the **change points** where
18 | you need to add tests).
19 |
20 | * Which **test points** did you find?
21 |
22 | * How much code will the unit tests using those **test points** cover?
23 |
24 | * What would be your choice? Which criteria would you use to make that choice?
25 |
26 | b. You might need to break some dependencies before you can write the unit tests.
27 |
28 | * Which parts of the code make it difficult to write unit tests?
29 |
30 | * Which dependency-breaking technique will you need to be able to write unit tests?
31 |
32 | * Does the chosen dependency-breaking technique depend on the chosen **test points**?
33 |
34 | * Imagine you are really in a hurry, what technique could you apply?
35 |
36 | * Imagine you are not so much in a hurry, what technique could you apply?
37 |
38 | 4. Once the existing behavior is protected, start using TDD to add the new behavior.
39 |
40 |
41 | #### Second iteration
42 |
43 | Modify the code so that the html file has automatically
44 | the same name as the input file and its written in the same place.
45 |
46 | Follow the same procedure that in the previous step:
47 |
48 | 1. Identify the **change points**.
49 |
50 | 2. Identify the **test points**.
51 |
52 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies).
53 |
54 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first).
55 |
56 | #### Third iteration
57 |
58 | Modify the code so that we can create a `HtmlTextConverter`
59 | that gets the input lines from a file,
60 | or create one that gets them from a user writing on a console.
61 |
62 | The program will consume lines written by the user until it detects
63 | a line containing only `:q` which will mean the end of lines.
64 |
65 | Follow the same procedure that in the previous step:
66 |
67 | 1. Identify the **change points**.
68 |
69 | 2. Identify the **test points**.
70 |
71 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies).
72 |
73 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first).
74 |
75 | #### Fourth iteration
76 |
77 | Modify the code so that we can create an object that converts the input text into markdown,
78 | or into HTML and write it to a file.
79 |
80 | Follow the same procedure that in the previous step:
81 |
82 | 1. Identify the **change points**.
83 |
84 | 2. Identify the **test points**.
85 |
86 | 3. Write unit tests to protect the existing code, (you might need to break some dependencies).
87 |
88 | 4. Use TDD to add the new behavior, (you might have to refactor the code a bit first).
89 |
90 | * How close are you from separating the different responsibilities in the class?
91 |
92 | This kata is based on Luca Minudel's [Text Converter exercise](https://github.com/lucaminudel/TDDwithMockObjectsAndDesignPrinciples/tree/master/TDDMicroExercises/Java/TextConverter)
93 | from his [TDDwithMockObjectsAndDesignPrinciples
94 | ](https://github.com/lucaminudel/TDDwithMockObjectsAndDesignPrinciples) exercises.
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.codesai.katas-java
5 | TextConverter
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | 1.8
10 | 1.8
11 |
12 |
13 |
14 |
15 | org.hamcrest
16 | hamcrest-all
17 | 1.3
18 |
19 |
20 |
21 | junit
22 | junit
23 | 4.13.1
24 |
25 |
26 |
27 | org.mockito
28 | mockito-all
29 | 2.0.2-beta
30 |
31 |
32 |
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/res/example.html:
--------------------------------------------------------------------------------
1 | Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10
Line 11
Line 12
Line 13
Line 14
Line 15
Line 16
Line 17
Line 18
Line 19
Line 20
Line 21
Line 22
Line 23
Line 24
Line 25
Line 26
Line 27
Line 28
Line 29
Line 30
Line 31
Line 32
Line 33
Line 34
Line 35
Line 36
Line 37
Line 38
Line 39
Line 40
Line 41
Line 42
Line 43
Line 44
Line 45
Line 46
Line 47
Line 48
Line 49
Line 50
Line 51
Line 52
Line 53
Line 54
Line 55
Line 56
Line 57
Line 58
Line 59
Line 60
Line 61
Line 62
Line 63
Line 64
Line 65
Line 66
Line 67
Line 68
Line 69
Line 70
Line 71
Line 72
Line 73
Line 74
Line 75
Line 76
Line 77
Line 78
Line 79
Line 80
Line 81
Line 82
Line 83
Line 84
Line 85
Line 86
Line 87
Line 88
Line 89
Line 90
Line 91
Line 92
Line 93
Line 94
Line 95
Line 96
Line 97
Line 98
Line 99
Line 100
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/res/example.txt:
--------------------------------------------------------------------------------
1 | Line 1
2 | Line 2
3 | Line 3
4 | Line 4
5 | Line 5
6 | Line 6
7 | Line 7
8 | Line 8
9 | Line 9
10 | Line 10
11 | Line 11
12 | Line 12
13 | Line 13
14 | Line 14
15 | Line 15
16 | Line 16
17 | Line 17
18 | Line 18
19 | Line 19
20 | Line 20
21 | Line 21
22 | Line 22
23 | Line 23
24 | Line 24
25 | Line 25
26 | Line 26
27 | Line 27
28 | Line 28
29 | Line 29
30 | Line 30
31 | Line 31
32 | Line 32
33 | Line 33
34 | Line 34
35 | Line 35
36 | Line 36
37 | Line 37
38 | Line 38
39 | Line 39
40 | Line 40
41 | Line 41
42 | Line 42
43 | Line 43
44 | Line 44
45 | Line 45
46 | Line 46
47 | Line 47
48 | Line 48
49 | Line 49
50 | Line 50
51 | Line 51
52 | Line 52
53 | Line 53
54 | Line 54
55 | Line 55
56 | Line 56
57 | Line 57
58 | Line 58
59 | Line 59
60 | Line 60
61 | Line 61
62 | Line 62
63 | Line 63
64 | Line 64
65 | Line 65
66 | Line 66
67 | Line 67
68 | Line 68
69 | Line 69
70 | Line 70
71 | Line 71
72 | Line 72
73 | Line 73
74 | Line 74
75 | Line 75
76 | Line 76
77 | Line 77
78 | Line 78
79 | Line 79
80 | Line 80
81 | Line 81
82 | Line 82
83 | Line 83
84 | Line 84
85 | Line 85
86 | Line 86
87 | Line 87
88 | Line 88
89 | Line 89
90 | Line 90
91 | Line 91
92 | Line 92
93 | Line 93
94 | Line 94
95 | Line 95
96 | Line 96
97 | Line 97
98 | Line 98
99 | Line 99
100 | Line 100
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/src/main/java/text_converter/HtmlTextConverter.java:
--------------------------------------------------------------------------------
1 | package text_converter;
2 |
3 | import java.io.*;
4 |
5 | public class HtmlTextConverter
6 | {
7 | private String fileName;
8 |
9 | public HtmlTextConverter(String fullFilenameWithPath)
10 | {
11 | this.fileName = fullFilenameWithPath;
12 | }
13 |
14 | public void convertToHtml(String htmlFile) throws IOException{
15 | BufferedWriter writer = new BufferedWriter(new FileWriter(htmlFile));
16 | BufferedReader reader = new BufferedReader(new FileReader(fileName));
17 |
18 | String line = reader.readLine();
19 | writer.write("");
20 | while (line != null)
21 | {
22 | String output = line;
23 | output = output.replace("&", "&");
24 | output = output.replace("<", "<");
25 | output = output.replace(">", ">");
26 | writer.write(output);
27 | writer.write("
");
28 | line = reader.readLine();
29 | }
30 | writer.write("");
31 | writer.close();
32 | }
33 |
34 | public String getFilename() {
35 | return this.fileName;
36 | }
37 |
38 | public static void main(String[] args) {
39 | try {
40 | HtmlTextConverter htmlTextConverter = new HtmlTextConverter("res/example.txt");
41 | htmlTextConverter.convertToHtml("res/example.html");
42 | } catch (Exception e) {
43 | e.printStackTrace();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/katas_java/08-doing-tdd-on-legacy-code/src/test/java/text_converter/.keepme:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/08-doing-tdd-on-legacy-code/src/test/java/text_converter/.keepme
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_1/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.codesai.katas-java
4 | outside-in-ohce-1
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 | jar
18 | test
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.13.1
25 | jar
26 | test
27 |
28 |
29 |
30 | org.mockito
31 | mockito-all
32 | 1.10.8
33 | test
34 |
35 |
36 |
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_1/readme.md:
--------------------------------------------------------------------------------
1 | # Outside-in TDD
2 |
3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html).
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_1/src/main/java/ohce/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_1/src/main/java/ohce/.keep
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_1/src/test/java/test/ohce/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_1/src/test/java/test/ohce/.keep
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_2/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.codesai.katas-java
4 | outside-in-ohce-2
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 | jar
18 | test
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.13.1
25 | jar
26 | test
27 |
28 |
29 |
30 | org.mockito
31 | mockito-all
32 | 1.10.8
33 | test
34 |
35 |
36 |
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_2/readme.md:
--------------------------------------------------------------------------------
1 | # Outside-in TDD
2 |
3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html).
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_2/src/main/java/ohce/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_2/src/main/java/ohce/.keep
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/part_2/src/test/java/test/ohce/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/09-outside-in-with-ohce-kata/part_2/src/test/java/test/ohce/.keep
--------------------------------------------------------------------------------
/katas_java/09-outside-in-with-ohce-kata/readme.md:
--------------------------------------------------------------------------------
1 | # Outside-in TDD
2 |
3 | In this session we'll practice outside-in TDD using test doubles with the [Ohce Kata](http://garajeando.blogspot.com.es/2016/05/the-ohce-kata-short-and-simple-exercise.html).
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .settings/
4 | bin/
5 | target
6 |
7 | .idea
8 | *.iml
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/README.md:
--------------------------------------------------------------------------------
1 | A subset of the [Bank Account kata](https://github.com/sandromancuso/Bank-kata).
2 |
3 | You have to write and make the following acceptance test pass using outside-in TDD:
4 |
5 | Given a client makes a deposit of 1000 on 10-01-2012
6 | And a deposit of 2000 on 13-01-2012
7 | And a withdrawal of 500 on 14-01-2012
8 |
9 | When she prints her bank statement
10 |
11 | Then she would see
12 |
13 | date || credit || debit || balance
14 | 14/01/2012 || || 500.00 || 2500.00
15 | 13/01/2012 || 2000.00 || || 3000.00
16 | 10/01/2012 || 1000.00 || || 1000.00
17 |
18 | The goal of the exercise is to understand the outside-in TDD style.
19 |
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.codesai.katas-java
4 | bank
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 | jar
18 | test
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.13.1
25 | jar
26 | test
27 |
28 |
29 |
30 | org.mockito
31 | mockito-all
32 | 1.10.8
33 | test
34 |
35 |
36 |
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/src/main/java/bank/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/10-outside-in-with-bank-kata/src/main/java/bank/.keep
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/acceptance/PrintingAccountStatementOnConsoleTest.java:
--------------------------------------------------------------------------------
1 | package bank.tests.acceptance;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.MatcherAssert.assertThat;
6 | import static org.hamcrest.Matchers.is;
7 |
8 | public class PrintingAccountStatementOnConsoleTest {
9 | @Test
10 | public void printing_statement_including_deposit_and_withdrawal() {
11 | // fix this test
12 | assertThat(true, is(false));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/unit/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/10-outside-in-with-bank-kata/src/test/java/bank/tests/unit/.keep
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .settings/
4 | bin/
5 | target
6 |
7 | .idea
8 | *.iml
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/README.md:
--------------------------------------------------------------------------------
1 | # Unusual Spending Kata
2 |
3 | You work at a credit card company and as a value-add they want to start providing alerts to users when their spending in any particular category is higher than usual.
4 |
5 | Each `Payment` comes with a `price`, `description`, and `category`. A `Category` represents a collection of payments (e. g. "entertainment", "restaurants", "rent", ...).
6 |
7 | This is what the new service should do:
8 |
9 | For a given user, you have to compare the total amount paid for the last two months, grouped by category; and filter down to the categories for which the user spent at least 50% more this month than last month, (you'll fetch from a DB all the user payments for the current month and the previous month).
10 |
11 | Then you have to compose an e-mail message to the user that lists the categories for which spending was unusually high, with a subject like: "Unusual spending of $1076 detected!" and a body like:
12 |
13 | ```
14 | Hello card user!
15 |
16 | We have detected unusually high spending on your card in these categories:
17 |
18 | * You spent $148 on groceries
19 | * You spent $928 on travel
20 |
21 | Love,
22 |
23 | The Credit Card Company
24 | ```
25 |
26 | [Original kata](https://github.com/testdouble/contributing-tests/wiki/Unusual-Spending-Kata)
27 |
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.codesai.katas-java
4 | unusual-spending-alert
5 | 0.0.1-SNAPSHOT
6 |
7 |
8 | 1.8
9 | 1.8
10 |
11 |
12 |
13 |
14 | org.hamcrest
15 | hamcrest-all
16 | 1.3
17 | jar
18 | test
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.13.1
25 | jar
26 | test
27 |
28 |
29 |
30 | org.mockito
31 | mockito-all
32 | 1.10.8
33 | test
34 |
35 |
36 |
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/src/main/java/alert_service/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/11-outside-in-with-unusual-spending-alert-service/src/main/java/alert_service/.keep
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/acceptance/AlertingUsersWithUnusualSpendingsTest.java:
--------------------------------------------------------------------------------
1 | package alert_service.tests.acceptance;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.hamcrest.MatcherAssert.assertThat;
6 | import static org.hamcrest.Matchers.is;
7 |
8 | public class AlertingUsersWithUnusualSpendingsTest {
9 | @Test
10 | public void alerting_users_with_unusual_spendings_in_some_categories() {
11 | // fix this test
12 | assertThat(true, is(false));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/unit/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codesai/practice-program-java/23c742138f7f743aa1997e817e2222b47b236758/katas_java/11-outside-in-with-unusual-spending-alert-service/src/test/java/alert_service/tests/unit/.keep
--------------------------------------------------------------------------------