diff --git a/chat/pom.xml b/chat/pom.xml index a61a9a2..c2f4ad0 100644 --- a/chat/pom.xml +++ b/chat/pom.xml @@ -54,8 +54,14 @@ ${junit.version} test + + org.json + json + 20240303 + + diff --git a/chat/src/main/java/module-info.java b/chat/src/main/java/module-info.java index d4b00e4..b4e5b23 100644 --- a/chat/src/main/java/module-info.java +++ b/chat/src/main/java/module-info.java @@ -5,6 +5,7 @@ module rtgre.chat { requires java.desktop; requires javafx.swing; requires net.synedra.validatorfx; + requires org.json; opens rtgre.chat to javafx.fxml; diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java index 6b307d5..aac96e3 100644 --- a/chat/src/main/java/rtgre/chat/ChatController.java +++ b/chat/src/main/java/rtgre/chat/ChatController.java @@ -23,8 +23,11 @@ import net.synedra.validatorfx.Check; import net.synedra.validatorfx.TooltipWrapper; import net.synedra.validatorfx.Validator; import rtgre.chat.graphisme.ContactListViewCell; +import rtgre.chat.graphisme.PostListViewCell; import rtgre.modeles.Contact; import rtgre.modeles.ContactMap; +import rtgre.modeles.Message; +import rtgre.modeles.Post; import java.awt.image.BufferedImage; import java.io.File; @@ -61,6 +64,7 @@ public class ChatController implements Initializable { public Contact contact; private ContactMap contactMap = new ContactMap(); private ObservableList contactObservableList = FXCollections.observableArrayList(); + private ObservableList postsObservableList = FXCollections.observableArrayList(); Validator validatorLogin = new Validator(); @@ -89,8 +93,12 @@ public class ChatController implements Initializable { avatarMenuItem.setOnAction(this::handleAvatarChange); avatarImageView.setOnMouseClicked(this::handleAvatarChange); + sendButton.setOnAction(this::onActionSend); initContactListView(); + initPostListView(); + contactsListView.getSelectionModel().selectedItemProperty().addListener( + (observableValue, previous, selected) -> handleContactSelection((Contact) selected)); validatorLogin.createCheck() .dependsOn("login", loginTextField.textProperty()) @@ -103,6 +111,15 @@ public class ChatController implements Initializable { /* -------------------------------------- */ loginTextField.setText("riri"); connectionButton.setSelected(true); + /* -------------------------------------- */ + } + + private void onActionSend(ActionEvent actionEvent) { + String login = getSelectedContactLogin(); + if (login != null) { + Message message = new Message(login, messageTextField.getText()); + LOGGER.info(message.toString()); + } } private void handleAvatarChange(Event event) { @@ -124,15 +141,12 @@ public class ChatController implements Initializable { private void handleConnection(Observable observable) { - /** - * - */ if (connectionButton.isSelected()) { java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null); this.contact = new Contact(loginTextField.getText(), img); contactMap.put(this.contact.getLogin(), this.contact); - System.out.println("Nouveau contact : " + contact); - System.out.println(contactMap); + LOGGER.info("Nouveau contact : " + contact); + LOGGER.info(contactMap.toString()); } } @@ -160,7 +174,7 @@ public class ChatController implements Initializable { Platform.runLater(() -> dateTimeLabel.setText(datetime)); Thread.sleep(60000); } catch (Exception e) { - System.out.println(e); + LOGGER.severe(e.getMessage()); } } @@ -181,4 +195,43 @@ public class ChatController implements Initializable { LOGGER.severe(e.getMessage()); } } + private void initPostListView() { + try { + postListView.setCellFactory(postListView -> new PostListViewCell(this)); + postListView.setItems(postsObservableList); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + } + } + + + public String getSelectedContactLogin() { + Contact contact; + String login; + try { + contact = (Contact) contactsListView.getSelectionModel().getSelectedItem(); + login = contact.getLogin(); + } catch (Exception e) { + login = null; + } + LOGGER.info("Selected login: " + login); + return login; + } + + public Contact getContact() { + return contact; + } + + public ContactMap getContactsMap() { + return contactMap; + } + + void handleContactSelection(Contact contactSelected) { + if (contactSelected != null) { + LOGGER.info("Clic sur " + contactSelected); + } + Post postSys = new Post("system", contactSelected.getLogin(), "Bienvenue dans la discussion avec " + contactSelected.getLogin()); + postsObservableList.add(postSys); + postListView.refresh(); + } } \ No newline at end of file diff --git a/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java new file mode 100644 index 0000000..164c21b --- /dev/null +++ b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java @@ -0,0 +1,88 @@ +package rtgre.chat.graphisme; + +import rtgre.chat.ChatController; +import javafx.embed.swing.SwingFXUtils; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.ListCell; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.scene.text.*; +import rtgre.modeles.Contact; +import rtgre.modeles.Post; + +import java.awt.image.BufferedImage; +import java.util.Date; + +public class PostListViewCell extends ListCell { + + /** Controller de l'application */ + ChatController controller; + + public PostListViewCell(ChatController controller) { + this.controller = controller; + } + + /** + * Callback déclenchée à chaque modification d'un objet d'une liste d'observable. + * + * @param post Le post + * @param empty La liste de cellule doit-elle être complètement remise à zéro ? + */ + @Override + protected void updateItem(Post post, boolean empty) { + super.updateItem(post, empty); + if (empty) { + setGraphic(null); + } + else { + updatePost(post); + } + } + + void updatePost(Post post) { + + Text datetimeText = new Text("\n%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS\n".formatted(new Date(post.getTimestamp()))); + datetimeText.setFont(Font.font(null, FontPosture.ITALIC, 8)); + + Text nicknameText = new Text(post.getFrom() + ": "); + nicknameText.setFill(Color.DARKBLUE); + nicknameText.setFont(Font.font(null, FontWeight.BOLD, 14)); + nicknameText.setFill(Color.BLUEVIOLET); + + Text msgText = new Text(post.getBody()); + + // L'émetteur du message + Contact c = this.controller.getContactsMap().get(post.getFrom()); + ImageView avatar = new ImageView(); + if (c != null) { + avatar = new ImageView(SwingFXUtils.toFXImage((BufferedImage) c.getAvatar(), null)); + avatar.setFitWidth(20); + avatar.setFitHeight(20); + } + + TextFlow tf = new TextFlow((Node) datetimeText, avatar, nicknameText, msgText); + tf.maxWidthProperty().bind(getListView().widthProperty().multiply(0.8)); + HBox hBox = new HBox(tf); + hBox.maxWidthProperty().bind(getListView().widthProperty()); + String login; + + try { + login = this.controller.getContact().getLogin(); + } + catch (Exception e) { + login = "???"; + } + + if (post.getFrom().equals(login)) { + tf.setBackground(Background.fill(Color.web("#EEF"))); + hBox.setAlignment(Pos.CENTER_RIGHT); + } else { + tf.setBackground(Background.fill(Color.web("#FEE"))); + hBox.setAlignment(Pos.CENTER_LEFT); + } + setGraphic(hBox); + getListView().scrollTo(getListView().getItems().size() - 1); + } +} \ No newline at end of file diff --git a/chat/src/main/java/rtgre/modeles/Contact.java b/chat/src/main/java/rtgre/modeles/Contact.java index cf3e786..549fefa 100644 --- a/chat/src/main/java/rtgre/modeles/Contact.java +++ b/chat/src/main/java/rtgre/modeles/Contact.java @@ -7,6 +7,8 @@ import java.io.File; import java.io.IOException; import java.util.Objects; +import static rtgre.chat.ChatApplication.LOGGER; + public class Contact { protected String login; protected java.awt.Image avatar; @@ -52,9 +54,9 @@ public class Contact { try { this.avatar = avatarFromLogin(banques_avatars, login); } catch (IOException e) { - System.out.println("Impossible de créer l'utilisateur " + login); - System.out.println(e.getMessage()); - System.out.println(banques_avatars); + LOGGER.severe("Impossible de créer l'utilisateur " + login); + LOGGER.severe(e.getMessage()); + LOGGER.severe(banques_avatars.getAbsolutePath()); } this.connected = connected; this.currentRoom = null; diff --git a/chat/src/main/java/rtgre/modeles/Message.java b/chat/src/main/java/rtgre/modeles/Message.java new file mode 100644 index 0000000..3f3f9de --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/Message.java @@ -0,0 +1,59 @@ +package rtgre.modeles; +import org.json.JSONObject; + + +public class Message { + /** + * Un message décrit sous la forme : + * @serialField : String to: Le destinataire + * @serialField : String body: le corps du message + */ + protected String to; + protected String body; + + public Message(String to, String body) { + + this.to = to; + this.body = body; + } + + public String getTo() { + return to; + } + + public String getBody() { + return body; + } + + @Override + public String toString() { + return "Message{" + + "to=" + to + + ", body=" + body + + '}'; + } + + public JSONObject toJsonObject() { + /** + * Transforme le message courant en objet JSON + */ + return new JSONObject() + .put("to", this.to) + .put("body", this.body); + } + + public String toJson() { + /** + * Transforme l'objet courant en String JSON + */ + return toJsonObject().toString(); + } + + public static Message fromJson(JSONObject json) { + /** + * Crée un objet message à partir d'un objet JSON + * @param: JSONObject json: l'objet JSON à transformer + */ + return new Message(json.getString("to"), json.getString("body")); + } +} diff --git a/chat/src/main/java/rtgre/modeles/Post.java b/chat/src/main/java/rtgre/modeles/Post.java new file mode 100644 index 0000000..188a917 --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/Post.java @@ -0,0 +1,107 @@ +package rtgre.modeles; + +import org.json.JSONObject; + +import java.util.Date; +import java.util.UUID; +public class Post extends Message { + + protected UUID id; + protected long timestamp; + protected String from; + + + public Post(UUID id, long timestamp, String from, String to, String body) { + /** + * Crée un objet Post + * @param: UUID id + * @param: long timestamp + * @param: String from + * @param: String to, + * @param: String body + */ + super(to, body); + this.id = id; + this.timestamp = timestamp; + this.from = from; + } + + public Post(String from, String to, String body) { + super(to, body); + this.from = from; + this.to = to; + this.body = body; + this.timestamp = new Date().getTime(); + this.id = UUID.randomUUID(); + } + + public Post(String from, Message message) { + super(message.to, message.body); + this.from = from; + this.to = message.to; + this.body = message.body; + this.timestamp = new Date().getTime(); + this.id = UUID.randomUUID(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Post post = (Post) o; + return id.equals(post.id); + } + + @Override + public int hashCode() { + return 0; + } + + public UUID getId() { + return id; + } + + public long getTimestamp() { + return timestamp; + } + + public String getFrom() { + return from; + } + + @Override + public String toString() { + return "Post{" + + "id=" + id + + ", timestamp=" + timestamp + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", body='" + body + '\'' + + '}'; + } + + @Override + public JSONObject toJsonObject() { + return new JSONObject() + .put("id", this.id) + .put("timestamp", this.timestamp) + .put("from", this.from) + .put("to", this.to) + .put("body", this.body); + } + + @Override + public String toJson() { + return toJsonObject().toString(); + } + + public static Post fromJson(JSONObject jsonObject) { + return new Post( + UUID.fromString(jsonObject.getString("id")), + jsonObject.getLong("timestamp"), + jsonObject.getString("from"), + jsonObject.getString("to"), + jsonObject.getString("body") + ); + } +} diff --git a/chat/src/main/java/rtgre/modeles/PostVector.java b/chat/src/main/java/rtgre/modeles/PostVector.java new file mode 100644 index 0000000..1db1105 --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/PostVector.java @@ -0,0 +1,27 @@ +package rtgre.modeles; + +import java.util.UUID; +import java.util.Vector; + +public class PostVector extends Vector { + + public Post getPostById(UUID uuid) { + for (Post post : this) { + if (post.id == uuid) { + return post; + } + } + return null; + } + + public Vector getPostsSince(long timestamp) { + Vector posts = new Vector<>(); + for (Post post : this) { + if (post.timestamp > timestamp) { + posts.add(post); + } + } + return posts; + } + +} diff --git a/chat/src/test/java/rtgre/modeles/MessageTest.java b/chat/src/test/java/rtgre/modeles/MessageTest.java new file mode 100644 index 0000000..3efcd8a --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/MessageTest.java @@ -0,0 +1,158 @@ +package rtgre.modeles; + +import org.json.JSONObject; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class MessageTest { + + static Class classe = Message.class; + static String module = "rtgre.modeles"; + + + @DisplayName("01-Structure") + @Nested + class StructureTest { + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + /** + * Attributs + */ + static Stream attributsProvider() { + return Stream.of( + arguments("to", "java.lang.String", Modifier.PROTECTED), + arguments("body", "java.lang.String", Modifier.PROTECTED) + ); + } + + @DisplayName("Déclaration des attributs : nom, type et visibilité") + @ParameterizedTest + @MethodSource("attributsProvider") + void testDeclarationAttributs(String nom, String type, int modifier) throws NoSuchFieldException { + Field field = classe.getDeclaredField(nom); + Assertions.assertEquals(type, field.getType().getName(), + "Type " + nom + " erroné : doit être " + type); + Assertions.assertEquals(modifier, field.getModifiers(), + "Visibilité " + nom + " erronée : doit être " + modifier); + + } + + /** + * Constructeurs + */ + static Stream constructeursProvider() { + return Stream.of( + arguments("public %s.Message(java.lang.String,java.lang.String)") + ); + } + + @DisplayName("Déclaration des constructeurs (base)") + @ParameterizedTest + @MethodSource("constructeursProvider") + void testConstructeurs(String signature) { + Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)), + String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s", + signature, constructeursSignatures)); + + } + + /** + * Méthodes + */ + static Stream methodesProvider() { + return Stream.of( + arguments("getTo", "public java.lang.String %s.Message.getTo()"), + arguments("getBody", "public java.lang.String %s.Message.getBody()"), + arguments("toString", "public java.lang.String %s.Message.toString()"), + arguments("toJsonObject", "public org.json.JSONObject %s.Message.toJsonObject()"), + arguments("toJSON", "public java.lang.String %s.Message.toJson()"), + arguments("fromJson", "public static %s.Message %s.Message.fromJson(org.json.JSONObject)") + ); + } + + @DisplayName("Déclaration des méthodes (base)") + @ParameterizedTest + @MethodSource("methodesProvider") + void testDeclarationMethodes(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + + } + + @DisplayName("02-Instanciation et getters") + @Test + void testGetToBody() { + String erreur = "Getter erroné"; + Message m = new Message("riri", "bonjour"); + assertEquals("riri", m.getTo(), erreur); + assertEquals("bonjour", m.getBody(), erreur); + } + + + @Test + @DisplayName("03-Représentation textuelle") + void testToString() { + Message m = new Message("riri", "bonjour"); + assertEquals("Message{to=riri, body=bonjour}", m.toString(), "Représentation textuelle erronée"); + } + + @Nested + @DisplayName("04-Représentation JSON") + class JSONTest { + + + @Test + @DisplayName("Objet JSON") + void testClesToJSONObject() { + Message m = new Message("riri", "bonjour"); + JSONObject obj = m.toJsonObject(); + Assertions.assertTrue(obj.has("to"), "Clé manquante"); + Assertions.assertTrue(obj.has("body"), "Clé manquante"); + + } + + + @Test + @DisplayName("Sérialisation de la représentation JSON") + void testToJson() { + Message m = new Message("riri", "bonjour"); + String chaine = m.toJson(); + Assertions.assertTrue(chaine.contains("\"to\":\"riri\""), "to erroné dans " + m); + Assertions.assertTrue(chaine.contains("\"body\":\"bonjour\""), "body erroné dans " + m); + } + + @Test + @DisplayName("Instanciation à partir d'un objet JSON") + void testMessageFromJSON() { + JSONObject json = new JSONObject("{\"to\":\"riri\",\"body\":\"bonjour\"}"); + Message m = Message.fromJson(json); + Assertions.assertEquals("riri", m.getTo(), "Constructeur erroné"); + Assertions.assertEquals("bonjour", m.getBody(), "Constructeur erroné"); + } + } +} \ No newline at end of file diff --git a/chat/src/test/java/rtgre/modeles/PostTest.java b/chat/src/test/java/rtgre/modeles/PostTest.java new file mode 100644 index 0000000..11c1ffe --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/PostTest.java @@ -0,0 +1,241 @@ +package rtgre.modeles; + +import org.json.JSONObject; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class PostTest { + + static Class classe = Post.class; + static String module = "rtgre.modeles"; + + @DisplayName("01-Structure") + @Nested + class StructureTest { + + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + + /** + * Attributs + */ + static Stream attributsHeritesProvider() { + return Stream.of( + arguments("to", "java.lang.String", Modifier.PROTECTED), + arguments("body", "java.lang.String", Modifier.PROTECTED) + ); + } + @DisplayName("Attributs hérités : nom, type et visibilité") + @ParameterizedTest + @MethodSource("attributsHeritesProvider") + void testDeclarationAttributsHerites(String nom, String type, int modifier) throws NoSuchFieldException { + Field[] fields = Post.class.getDeclaredFields(); + List noms = Arrays.stream(fields).map(Field::getName).toList(); + Assertions.assertFalse(noms.contains(nom), nom + " doit être hérité"); + } + + + /** Attributs */ + static Stream attributsProvider() { + return Stream.of( + arguments("id", "java.util.UUID", Modifier.PROTECTED), + arguments("timestamp", "long", Modifier.PROTECTED), + arguments("from", "java.lang.String", Modifier.PROTECTED) + ); + } + @DisplayName("Déclaration des attributs : nom, type et visibilité") + @ParameterizedTest + @MethodSource("attributsProvider") + void testDeclarationAttributs(String nom, String type, int modifier) throws NoSuchFieldException { + Field field = classe.getDeclaredField(nom); + Assertions.assertEquals(type, field.getType().getName(), + "Type " + nom + " erroné : doit être " + type); + Assertions.assertEquals(modifier, field.getModifiers(), + "Visibilité " + nom + " erronée : doit être " + modifier); + + } + + @DisplayName("Héritage") + @Test + void testHeritage() { + Post p = new Post("riri", "fifi", "bonjour"); + Assertions.assertInstanceOf(Message.class, p, "Post doit hériter de Message"); + } + + /** Constructeurs */ + static Stream constructeursProvider() { + return Stream.of( + arguments("public %s.Post(java.util.UUID,long,java.lang.String,java.lang.String,java.lang.String)"), + arguments("public %s.Post(java.lang.String,java.lang.String,java.lang.String)") + ); + } + @DisplayName("Déclaration des constructeurs (base)") + @ParameterizedTest + @MethodSource("constructeursProvider") + void testConstructeurs(String signature) { + Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)), + String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s", + signature, constructeursSignatures)); + + } + + + /** Méthodes */ + static Stream methodesProvider1() { + return Stream.of( + arguments("getId", "public java.util.UUID %s.Post.getId()"), + arguments("getTimestamp", "public long %s.Post.getTimestamp()"), + arguments("getFrom", "public java.lang.String %s.Post.getFrom()"), + arguments("toString", "public java.lang.String %s.Post.toString()"), + arguments("toJsonObject", "public org.json.JSONObject %s.Post.toJsonObject()"), + arguments("toJSON", "public java.lang.String %s.Post.toJson()"), + arguments("fromJson", "public static %s.Post %s.Post.fromJson(org.json.JSONObject)"), + arguments("equals", "public boolean %s.Post.equals(java.lang.Object)") + ); + } + @DisplayName("Déclaration des méthodes (base)") + @ParameterizedTest + @MethodSource("methodesProvider1") + void testDeclarationMethodes1(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + } + + @DisplayName("02-Instanciation et getter") + @Nested + class InstanciationPostTest { + + @DisplayName("Constructeur par défaut") + @Test + void testConstructeurDefaut() { + UUID id = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + Post p = new Post(id, 700000, "riri", "fifi", "bonjour"); + Assertions.assertEquals(id, p.getId(), "id erroné"); + Assertions.assertEquals(700000, p.getTimestamp(), "timestamp erroné"); + Assertions.assertEquals("riri", p.getFrom(), "from erroné"); + Assertions.assertEquals("fifi", p.getTo(), "to erroné"); + Assertions.assertEquals("bonjour", p.getBody(), "body erroné"); + } + + @DisplayName("Constructeur avec choix id, timestamp") + @Test + void testConstructeur() { + Post p = new Post("fifi", "riri", "salut"); + Assertions.assertEquals("fifi", p.getFrom(), "from erroné"); + Assertions.assertEquals("riri", p.getTo(), "to erroné"); + Assertions.assertEquals("salut", p.getBody(), "body erroné"); + } + + } + + @Test + @DisplayName("03-Représentation textuelle d'un post") + void testToString() { + UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + Post p = new Post(uuid, 17297794, "riri", "fifi", "bonjour"); + String chaine = p.toString(); + Assertions.assertEquals("Post{id=a821f534-b63a-4006-bbe1-eab7c4ff834e, timestamp=17297794, " + + "from='riri', to='fifi', body='bonjour'}", chaine, "Représentation textuelle erronée"); + } + + + @DisplayName("04-Représentation JSON") + @Nested + class TestJSON { + + @Test + @DisplayName("Objet JSON représentant un post") + void toJsonObject () { + UUID uuid = UUID.randomUUID(); + Post p = new Post(uuid, 800000, "riri", "fifi", "bonjour"); + JSONObject obj = p.toJsonObject(); + Assertions.assertTrue(obj.has("id"), "Clé manquante"); + Assertions.assertTrue(obj.has("timestamp"), "Clé manquante"); + Assertions.assertTrue(obj.has("from"), "Clé manquante"); + Assertions.assertTrue(obj.has("to"), "Clé manquante"); + Assertions.assertTrue(obj.has("body"), "Clé manquante"); + /*JSONAssert.assertEquals("{id:1}", obj, true);*/ + } + + @Test + @DisplayName("Sérialisation de la représentation JSON") + void testToJson () { + UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + Post p = new Post(uuid, 17297794, "bob", "alice", "bonjour"); + String chaine = p.toJson(); + Assertions.assertTrue(chaine.contains("\"from\":\"bob\""), "from erroné dans " + p); + Assertions.assertTrue(chaine.contains("\"to\":\"alice\""), "to erroné dans " + p); + Assertions.assertTrue(chaine.contains("\"body\":\"bonjour\""), "body erroné dans " + p); + Assertions.assertTrue(chaine.contains("\"timestamp\":17297794"), "timestamp erroné dans " + p); + Assertions.assertTrue(chaine.contains("\"id\":\"a821f534-b63a-4006-bbe1-eab7c4ff834e\""), "id erroné dans " + p.toJson()); + + } + + @Test + @DisplayName("Construction à partir d'un JSON") + void testInstanciationJSON() { + JSONObject json = new JSONObject("{\"id\": \"a821f534-b63a-4006-bbe1-eab7c4ff834e\",\"timestamp\":17297794,\"to\":\"riri\",\"from\":\"fifi\",\"body\":\"ouf\"}"); + Post p = Post.fromJson(json); + UUID uuid = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + Assertions.assertEquals(uuid, p.getId(), "id erroné"); + Assertions.assertEquals(17297794, p.getTimestamp(), "timestamp erroné"); + Assertions.assertEquals("riri", p.getTo(), "to erroné"); + Assertions.assertEquals("fifi", p.getFrom(), "from erroné"); + Assertions.assertEquals("ouf", p.getBody(), "body erroné"); + } + + } + + @DisplayName("05-Traitement des posts par le serveur") + @Nested + class TestTraitementPost { + + @Test + @DisplayName("Instanciation à partir d'un message") + void testInstanciationFromMessage() { + Message m = new Message("bob", "hello"); + Post p = new Post("alice", m); + Assertions.assertEquals("alice", p.getFrom(), "From erroné"); + Assertions.assertEquals("bob", p.getTo(), "To erroné"); + Assertions.assertEquals("hello", p.getBody(), "Body erroné"); + } + + + @Test + @DisplayName("Egalité de posts") + void testEquals() { + Post p1 = new Post("riri", "fifi", "salut"); + Post p2 = new Post(p1.id, 17297794, "riri", "fifi", "bonjour"); + Post p3 = new Post("riri", "fifi", "salut"); + Assertions.assertEquals(p1, p2, "Erreur de comparaison"); + Assertions.assertNotEquals(p1, p3, "Erreur de comparaison"); + Assertions.assertNotEquals(p2, p3, "Erreur de comparaison"); + + } + } +} \ No newline at end of file diff --git a/chat/src/test/java/rtgre/modeles/PostVectorTest.java b/chat/src/test/java/rtgre/modeles/PostVectorTest.java new file mode 100644 index 0000000..81affa9 --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/PostVectorTest.java @@ -0,0 +1,112 @@ +package rtgre.modeles; + +import javafx.geometry.Pos; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.Vector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class PostVectorTest { + + static Class classe = PostVector.class; + static String module = "rtgre.modeles"; + + @DisplayName("01-Structure") + @Nested + class Structure { + + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + @Test + @DisplayName("Heritage") + void testHeritage() { + PostVector pv = new PostVector(); + assertInstanceOf(Vector.class, pv, "Doit hériter de Vector"); + } + + @Test + @DisplayName("Nbre attributs") + void testDeclarationNbreAttributs() { + Field[] fields = classe.getDeclaredFields(); + assertEquals(0, fields.length, "Ne doit pas posséder d'attributs"); + } + + /** Méthodes */ + static Stream methodesProvider1() { + return Stream.of( + arguments("getPostById", "public %s.Post rtgre.modeles.PostVector.getPostById(java.util.UUID)"), + arguments("getPostsSince", "public java.util.Vector %s.PostVector.getPostsSince(long)") + ); + } + @DisplayName("Déclaration des méthodes (base)") + @ParameterizedTest + @MethodSource("methodesProvider1") + void testDeclarationMethodes1(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + + + } + + @DisplayName("02-Méthodes") + @Nested + class MethodesPostVectorTest { + @Test + void getPostById() { + PostVector pv = new PostVector(); + UUID uuid1 = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + UUID uuid2 = UUID.fromString("b932f534-b63a-4006-bbe1-eab7c4ff834e"); + Post p1 = new Post(uuid1, 70000, "fifi", "riri", "message"); + Post p2 = new Post(uuid2, 70000, "donald", "mickey", "message"); + + pv.add(new Post("fifi", "riri", "message")); + pv.add(new Post("donald", "mickey", "message")); + pv.add(p1); + pv.add(new Post("fifi", "riri", "message")); + pv.add(p2); + + Assertions.assertEquals(p1, pv.getPostById(uuid1), "Récupération post erronée"); + Assertions.assertEquals(p2, pv.getPostById(uuid2), "Récupération post erronée"); + } + + @Test + void getPostsSince() { + PostVector pv = new PostVector(); + pv.add(new Post(UUID.randomUUID(), 70000, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 70010, "donald", "mickey", "message")); + pv.add(new Post(UUID.randomUUID(), 70020, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 80000, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 80020, "fifi", "riri", "message")); + + Vector expected = new Vector<>(); + expected.add(pv.get(2)); expected.add(pv.get(3)); expected.add(pv.get(4)); + + Vector res = pv.getPostsSince(70015); + assertEquals(expected, res, "Extraction erronée"); + } + } +} \ No newline at end of file