top10: 1.1.2 - (RED) Explicitly formulated expectations for `Ranking`
[demos/kafka/wordcount] / src / test / java / de / juplo / kafka / wordcount / top10 / RankingTest.java
1 package de.juplo.kafka.wordcount.top10;
2
3 import org.junit.jupiter.api.DisplayName;
4 import org.junit.jupiter.api.Test;
5 import org.junit.jupiter.params.ParameterizedTest;
6 import org.junit.jupiter.params.provider.MethodSource;
7 import org.junit.jupiter.params.provider.ValueSource;
8
9 import java.util.LinkedList;
10 import java.util.List;
11 import java.util.stream.Stream;
12
13 import static org.assertj.core.api.Assertions.assertThat;
14 import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
15
16
17 public class RankingTest
18 {
19   @DisplayName("A newly created instance is empty")
20   @Test
21   public void testNewRankingIsEmpty()
22   {
23     Ranking ranking = new Ranking();
24     assertThat(ranking.getEntries()).isEmpty();
25   }
26
27   @DisplayName("An instance that was build from an empty ranking is empty")
28   @Test
29   public void testRankingOfYieldsExpectedResultForEmptyList()
30   {
31     Ranking ranking = new Ranking();
32     assertThat(ranking.getEntries()).isEmpty();
33   }
34
35   @DisplayName("An instance that was build from a valid ranking contains the expected entries")
36   @ParameterizedTest
37   @MethodSource("validRankingsProvider")
38   public void testRankingOfYieldsExpectedResultsForValidRankings(List<Entry> entryList)
39   {
40     Ranking ranking = Ranking.of(toArray(entryList));
41     assertThat(ranking.getEntries()).containsExactlyElementsOf(entryList);
42   }
43
44   @DisplayName("The builder fails for invalid rankings")
45   @ParameterizedTest
46   @MethodSource("invalidRankingsProvider")
47   public void testRankingOfThrowsExceptionForInvalidRankings(List<Entry> entryList)
48   {
49     assertThatExceptionOfType(IllegalArgumentException.class)
50         .isThrownBy(() -> Ranking.of(toArray(entryList)));
51   }
52
53   @DisplayName("Adding a new word with highest ranking, pushes all other words down")
54   @ParameterizedTest
55   @MethodSource("validRankingsProvider")
56   public void testAddingNewWordWithHighestRanking(List<Entry> entryList)
57   {
58     Ranking ranking = Ranking.of(toArray(entryList));
59     Entry newEntry = Entry.of("NEW!", rankingForPosition(-1));
60     ranking.add(newEntry);
61     assertThat(ranking.getEntries()[0]).isEqualTo(newEntry);
62     for (int i = 0; i < entryList.size() && i < Ranking.MAX_ENTRIES - 1; i++)
63     {
64       assertThat(ranking.getEntries()[i + 1]).isEqualTo(entryList.get(i));
65     }
66   }
67
68   @DisplayName("Adding a new word with an existent ranking, pushes all words with lower ranking down")
69   @ParameterizedTest
70   @MethodSource("validRankingsProvider")
71   public void testAddingNewWordWithExistingRanking(List<Entry> entryList)
72   {
73     for (int position = 0; position < entryList.size(); position++ )
74     {
75       Ranking ranking = Ranking.of(toArray(entryList));
76       Entry newEntry = Entry.of("NEW!", rankingForPosition(position));
77       ranking.add(newEntry);
78       for (int i = 0; i < entryList.size() && i < Ranking.MAX_ENTRIES - 1; i++)
79       {
80         if (i < position)
81         {
82           assertThat(ranking.getEntries()[i]).isEqualTo(entryList.get(i));
83         }
84         if (i == position)
85         {
86           assertThat(ranking.getEntries()[i]).isEqualTo(entryList.get(i));
87           assertThat(ranking.getEntries()[i + 1]).isEqualTo(newEntry);
88         }
89         if (i > position)
90         {
91           assertThat(ranking.getEntries()[i + 1]).isEqualTo(entryList.get(i));
92         }
93       }
94     }
95   }
96
97   @DisplayName("Adding a highest ranking for an existing word shifts it to the first place")
98   @ParameterizedTest
99   @ValueSource(ints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })
100   public void testAddingExistingWordWithHighestRanking(int position)
101   {
102     Ranking ranking = Ranking.of(toArray(VALID_RANKINGS[0]));
103     String word = wordForPosition(position);
104     Entry highestEntry = Entry.of(word, 100l);
105     ranking.add(highestEntry);
106     List<Entry> expectedEntries = Stream
107         .concat(
108             Stream.of(highestEntry),
109             VALID_RANKINGS[0]
110                 .stream()
111                 .filter(entry -> !entry.getWord().equals(word)))
112         .toList();
113     assertThat(ranking.getEntries()).containsExactlyElementsOf(expectedEntries);
114   }
115
116   @DisplayName("Adding an existing word with unchanged ranking changes nothing")
117   @ParameterizedTest
118   @ValueSource(ints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })
119   public void testAddingExistingWordWithUnchangedRanking(int position)
120   {
121     Ranking ranking = Ranking.of(toArray(VALID_RANKINGS[0]));
122     Entry unchangedEntry = Entry.of(
123         wordForPosition(position),
124         rankingForPosition(position));
125     ranking.add(unchangedEntry);
126     assertThat(ranking.getEntries()).containsExactlyElementsOf(VALID_RANKINGS[0]);
127   }
128
129   @DisplayName("Adding an existing word with a lower ranking fails")
130   @ParameterizedTest
131   @MethodSource("validRankingsProvider")
132   public void testAddingExistingWordWithLowerRankingFails(List<Entry> entryList)
133   {
134     Ranking ranking = Ranking.of(toArray(entryList));
135     entryList.forEach(entry ->
136       assertThatExceptionOfType(IllegalArgumentException.class)
137           .isThrownBy(() -> ranking.add(Entry.of(entry.getWord(), entry.getCounter() - 1))));
138   }
139
140
141   Entry[] toArray(List<Entry> entryList)
142   {
143     return entryList.toArray(size -> new Entry[size]);
144   }
145
146   static String wordForPosition(int position)
147   {
148     return Integer.toString(position+1);
149   }
150
151   static long rankingForPosition(int position)
152   {
153     return (long)Ranking.MAX_ENTRIES * 2 - position;
154   }
155
156   static Stream<List<Entry>> validRankingsProvider()
157   {
158     return Stream.of(VALID_RANKINGS);
159   }
160
161   static Stream<List<Entry>> invalidRankingsProvider()
162   {
163     return Stream.of(INVALID_RANKINGS);
164   }
165
166   static String[] WORDS = new String[Ranking.MAX_ENTRIES];
167   static List<Entry>[] VALID_RANKINGS = new List[Ranking.MAX_ENTRIES];
168
169   static
170   {
171     for (int i = 0; i < Ranking.MAX_ENTRIES; i++)
172     {
173       List<Entry> ranking = new LinkedList<>();
174       String word = null;
175       for (int position = 0; position <= i; position++)
176       {
177         word = wordForPosition(position);
178         Entry entry = Entry.of(word, rankingForPosition(position));
179         ranking.add(entry);
180       }
181       WORDS[i] = word;
182       VALID_RANKINGS[Ranking.MAX_ENTRIES - (i + 1)] = ranking;
183     }
184   }
185
186   static List<Entry>[] INVALID_RANKINGS = new List[] {
187       List.of(
188           Entry.of("Platz eins", 1l),
189           Entry.of("Platz zwei", 2l)),
190       List.of(
191           Entry.of("Platz eins", 1111111111l),
192           Entry.of("Platz zwei", 222222222l),
193           Entry.of("Platz eins", 1l)),
194       List.of(
195           Entry.of("Platz eins", 11l),
196           Entry.of("Platz eins", 1l)),
197       List.of(
198           Entry.of("Platz eins", 1111111111l),
199           Entry.of("Platz zwei", 222222222l),
200           Entry.of("Platz eins", 11111111l),
201           Entry.of("Platz zwei", 2222222l),
202           Entry.of("Platz fünf", 555555l)),
203       List.of(
204           Entry.of("Platz eins", 1111111111l),
205           Entry.of("Platz zwei", 222222222l),
206           Entry.of("Platz drei", 33333333l),
207           Entry.of("Platz vier", 4444444l),
208           Entry.of("Platz eins", 111111l),
209           Entry.of("Platz sechs", 66666l)),
210       List.of(
211           Entry.of("Platz eins", 1111111111l),
212           Entry.of("Platz zwei", 222222222l),
213           Entry.of("Platz drei", 33333333l),
214           Entry.of("Platz vier", 4444444l),
215           Entry.of("Platz fünf", 555555l),
216           Entry.of("Platz sechs", 66666l),
217           Entry.of("Platz eins", 1l)),
218       List.of(
219           Entry.of("Platz eins", 1111111111l),
220           Entry.of("Platz zwei", 222222222l),
221           Entry.of("Platz drei", 33333333l),
222           Entry.of("Platz vier", 4444444l),
223           Entry.of("Platz fünf", 555555l),
224           Entry.of("Platz sechs", 66666l),
225           Entry.of("Platz sieben", 7777l),
226           Entry.of("Platz acht", 888l),
227           Entry.of("Platz neun", 99l),
228           Entry.of("Platz 10", 6l),
229           Entry.of("Platz 11", 3l))};
230 }