diff --git a/pom.xml b/pom.xml
index 087afdf..1a49bf4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,34 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.projectlombok
+ lombok
+ 1.18.38
+ provided
+
+
+ org.springframework.data
+ spring-data-jpa
+
@@ -54,6 +82,19 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+ -XX:+EnableDynamicAgentLoading
+ -Djdk.instrument.traceUsage=false
+ --add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.util=ALL-UNNAMED
+
+
+
diff --git a/src/main/java/net/tokishu/note/controller/NotesController.java b/src/main/java/net/tokishu/note/controller/NotesController.java
index 17d34e8..d417e56 100644
--- a/src/main/java/net/tokishu/note/controller/NotesController.java
+++ b/src/main/java/net/tokishu/note/controller/NotesController.java
@@ -1,11 +1,17 @@
package net.tokishu.note.controller;
+import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note;
import net.tokishu.note.service.NoteService;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
@RestController
public class NotesController {
@@ -14,27 +20,30 @@ public class NotesController {
private NoteService noteService;
@GetMapping("/notes")
- public List getAll(){
- return noteService.getAll();
+ public ResponseEntity> getAll(){
+ return ResponseEntity.ok(noteService.getAll());
}
- @GetMapping("/note/{id}")
- public Note find(@PathVariable Long id){
- return noteService.find(id);
+ @GetMapping("/note/{uuid}")
+ public Note find(@PathVariable UUID uuid){
+ return noteService.find(uuid);
}
@PostMapping("/note")
- public Note add(@RequestBody Note note){
- return noteService.add(note);
+ public ResponseEntity> add(@RequestBody NoteRequest note){
+ noteService.add(note);
+ return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "Note added"));
}
- @PutMapping("/note/{id}")
- public Note update(@PathVariable Long id, @RequestBody Note note){
- return noteService.update(id, note);
+ @PutMapping("/note/{uuid}")
+ public ResponseEntity> update(@PathVariable("uuid") UUID uuid, @RequestBody NoteRequest note){
+ noteService.update(uuid, note);
+ return ResponseEntity.ok(Map.of("message", "Note updated"));
}
- @DeleteMapping("/note/{id}")
- public void delete(@PathVariable Long id){
- noteService.delete(id);
+ @DeleteMapping("/note/{uuid}")
+ public ResponseEntity> delete(@PathVariable UUID uuid){
+ noteService.delete(uuid);
+ return ResponseEntity.ok(Map.of(" message", "Note deleted"));
}
}
diff --git a/src/main/java/net/tokishu/note/dto/NoteRequest.java b/src/main/java/net/tokishu/note/dto/NoteRequest.java
new file mode 100644
index 0000000..83a1cc8
--- /dev/null
+++ b/src/main/java/net/tokishu/note/dto/NoteRequest.java
@@ -0,0 +1,9 @@
+package net.tokishu.note.dto;
+
+import lombok.Data;
+
+@Data
+public class NoteRequest {
+ private String name;
+ private String text;
+}
diff --git a/src/main/java/net/tokishu/note/model/Note.java b/src/main/java/net/tokishu/note/model/Note.java
index d14f3ba..514ec75 100644
--- a/src/main/java/net/tokishu/note/model/Note.java
+++ b/src/main/java/net/tokishu/note/model/Note.java
@@ -1,46 +1,18 @@
package net.tokishu.note.model;
+import jakarta.persistence.*;
+import lombok.Data;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.UUID;
+
+@Entity
+@Data
+@Table(name = "Notes")
public class Note {
- private Long id;
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID uuid;
private String name;
private String text;
-
- public Note(Long id, String name, String text) {
- this.id = id;
- this.name = name;
- this.text = text;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getText() {
- return text;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
- @Override
- public String toString() {
- return "Note{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", text='" + text + '\'' +
- '}';
- }
}
\ No newline at end of file
diff --git a/src/main/java/net/tokishu/note/repo/NoteRepository.java b/src/main/java/net/tokishu/note/repo/NoteRepository.java
new file mode 100644
index 0000000..c13f9ab
--- /dev/null
+++ b/src/main/java/net/tokishu/note/repo/NoteRepository.java
@@ -0,0 +1,8 @@
+package net.tokishu.note.repo;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.UUID;
+
+public interface NoteRepository extends JpaRepository {
+}
diff --git a/src/main/java/net/tokishu/note/service/NoteService.java b/src/main/java/net/tokishu/note/service/NoteService.java
index cc6dcce..5bff837 100644
--- a/src/main/java/net/tokishu/note/service/NoteService.java
+++ b/src/main/java/net/tokishu/note/service/NoteService.java
@@ -1,52 +1,52 @@
package net.tokishu.note.service;
+import lombok.RequiredArgsConstructor;
+import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note;
+import net.tokishu.note.repo.NoteRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NoSuchElementException;
+import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
@Service
+@RequiredArgsConstructor
public class NoteService {
- private List notes = new ArrayList<>();
- private AtomicLong idGenerator = new AtomicLong(1);
+
+ public final NoteRepository noteRepository;
public List getAll(){
- return notes;
+ return noteRepository.findAll();
}
- public Note find(Long id) {
- for (Note note : notes) {
- if (note.getId().equals(id)) {
- return note;
- }
- }
- throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found");
+ public Note find(UUID uuid) {
+ return noteRepository.findById(uuid)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
}
- public Note add(Note note){
- note.setId(idGenerator.getAndIncrement());
- notes.add(note);
- return note;
+ public Note add(NoteRequest data){
+ Note note = new Note();
+ note.setName(data.getName());
+ note.setText(data.getText());
+
+ return noteRepository.save(note);
}
- public Note update(Long id, Note updatedNote){
- for (Note note : notes){
- if (note.getId().equals(id)) {
- note.setName(updatedNote.getName());
- note.setText(updatedNote.getText());
- return note;
- }
- }
- throw new NoSuchElementException("Note not found");
+ public Note update(UUID uuid, NoteRequest data){
+ Note existing = noteRepository.findById(uuid)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
+ existing.setName(data.getName());
+ existing.setText(data.getText());
+
+ return noteRepository.save(existing);
}
- public void delete(Long id){
- notes.removeIf(note -> note.getId().equals(id));
+ public void delete(UUID uuid){
+ Note existing = noteRepository.findById(uuid)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
+ noteRepository.deleteById(uuid);
}
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 03d0d49..a539af7 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,2 +1,11 @@
spring.application.name=notes
-spring.mvc.servlet.path=/api
\ No newline at end of file
+spring.mvc.servlet.path=/api
+
+# DB
+spring.datasource.url=jdbc:postgresql://localhost:5432/notes
+spring.datasource.username=postgres
+spring.datasource.password=password
+spring.jpa.hibernate.ddl-auto=update
+
+# Disable trace
+server.error.include-stacktrace=never
\ No newline at end of file
diff --git a/src/test/java/net/tokishu/note/service/NoteServiceTest.java b/src/test/java/net/tokishu/note/service/NoteServiceTest.java
index df32cb2..bffaa87 100644
--- a/src/test/java/net/tokishu/note/service/NoteServiceTest.java
+++ b/src/test/java/net/tokishu/note/service/NoteServiceTest.java
@@ -1,63 +1,202 @@
package net.tokishu.note.service;
+import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note;
+import net.tokishu.note.repo.NoteRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.server.ResponseStatusException;
import java.util.List;
+import java.util.UUID;
+
import static org.junit.jupiter.api.Assertions.*;
+@SpringBootTest
+@ActiveProfiles("test") // Используем тестовый профиль
+@Transactional // Откатывает транзакции после каждого теста
public class NoteServiceTest {
+ @Autowired
private NoteService noteService;
+ @Autowired
+ private NoteRepository noteRepository;
+
@BeforeEach
- void setUp(){
- noteService = new NoteService();
+ void setUp() {
+ // Очищаем БД перед каждым тестом
+ noteRepository.deleteAll();
}
@Test
- void addAndGetAllNotes(){
- Note note = new Note(1L, "QQ", "Test content");
- noteService.add(note);
+ void addAndGetAllNotes() {
+ // Given
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setName("Test Note");
+ noteRequest.setText("Test content");
+ // When
+ Note savedNote = noteService.add(noteRequest);
List notes = noteService.getAll();
+
+ // Then
+ assertNotNull(savedNote);
+ assertNotNull(savedNote.getUuid());
assertEquals(1, notes.size());
- assertEquals("QQ", notes.get(0).getName());
+ assertEquals("Test Note", notes.get(0).getName());
+ assertEquals("Test content", notes.get(0).getText());
}
@Test
- void updateNote(){
- Note note = new Note(1L, "Old QQ", "Old test content");
- noteService.add(note);
+ void findExistingNote() {
+ // Given
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setName("Find Me");
+ noteRequest.setText("Find content");
+ Note savedNote = noteService.add(noteRequest);
- Note updatedNote = new Note(1L, "New day, new qq :3", "New test content");
- noteService.update(1L, updatedNote);
+ // When
+ Note foundNote = noteService.find(savedNote.getUuid());
- Note result = noteService.getAll().get(0);
- assertEquals("New day, new qq :3", result.getName());
+ // Then
+ assertNotNull(foundNote);
+ assertEquals("Find Me", foundNote.getName());
+ assertEquals("Find content", foundNote.getText());
+ assertEquals(savedNote.getUuid(), foundNote.getUuid());
}
@Test
- void deleteNote(){
- Note noteToDelete = new Note(1L, "Bye bye :(", "content");
- noteService.add(noteToDelete);
- noteService.delete(1L);
+ void findNonExistingNote_ShouldThrowException() {
+ // Given
+ UUID nonExistingId = UUID.randomUUID();
- assertTrue(noteService.getAll().isEmpty());
+ // When & Then
+ ResponseStatusException exception = assertThrows(
+ ResponseStatusException.class,
+ () -> noteService.find(nonExistingId)
+ );
+
+ assertTrue(exception.getMessage().contains("Note not found"));
}
@Test
- void findNote(){
+ void updateExistingNote() {
+ // Given
+ NoteRequest originalRequest = new NoteRequest();
+ originalRequest.setName("Original Name");
+ originalRequest.setText("Original content");
+ Note savedNote = noteService.add(originalRequest);
- for (long i = 1; i <= 10; i++) {
- Note note = new Note(i, "Note " + i, "Content for note №" + i);
- noteService.add(note);
+ NoteRequest updateRequest = new NoteRequest();
+ updateRequest.setName("Updated Name");
+ updateRequest.setText("Updated content");
+
+ // When
+ Note updatedNote = noteService.update(savedNote.getUuid(), updateRequest);
+
+ // Then
+ assertNotNull(updatedNote);
+ assertEquals(savedNote.getUuid(), updatedNote.getUuid());
+ assertEquals("Updated Name", updatedNote.getName());
+ assertEquals("Updated content", updatedNote.getText());
+
+ // Проверяем, что изменения сохранились в БД
+ Note foundNote = noteService.find(savedNote.getUuid());
+ assertEquals("Updated Name", foundNote.getName());
+ assertEquals("Updated content", foundNote.getText());
+ }
+
+ @Test
+ void updateNonExistingNote_ShouldThrowException() {
+ // Given
+ UUID nonExistingId = UUID.randomUUID();
+ NoteRequest updateRequest = new NoteRequest();
+ updateRequest.setName("Update Name");
+ updateRequest.setText("Update content");
+
+ // When & Then
+ ResponseStatusException exception = assertThrows(
+ ResponseStatusException.class,
+ () -> noteService.update(nonExistingId, updateRequest)
+ );
+
+ assertTrue(exception.getMessage().contains("Note not found"));
+ }
+
+ @Test
+ void deleteExistingNote() {
+ // Given
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setName("Delete Me");
+ noteRequest.setText("Delete content");
+ Note savedNote = noteService.add(noteRequest);
+
+ // When
+ assertDoesNotThrow(() -> noteService.delete(savedNote.getUuid()));
+
+ // Then
+ List notes = noteService.getAll();
+ assertTrue(notes.isEmpty());
+
+ // Проверяем, что заметка действительно удалена
+ ResponseStatusException exception = assertThrows(
+ ResponseStatusException.class,
+ () -> noteService.find(savedNote.getUuid())
+ );
+ assertTrue(exception.getMessage().contains("Note not found"));
+ }
+
+ @Test
+ void deleteNonExistingNote_ShouldThrowException() {
+ // Given
+ UUID nonExistingId = UUID.randomUUID();
+
+ // When & Then
+ ResponseStatusException exception = assertThrows(
+ ResponseStatusException.class,
+ () -> noteService.delete(nonExistingId)
+ );
+
+ assertTrue(exception.getMessage().contains("Note not found"));
+ }
+
+ @Test
+ void getAllNotes_WhenEmpty() {
+ // When
+ List notes = noteService.getAll();
+
+ // Then
+ assertNotNull(notes);
+ assertTrue(notes.isEmpty());
+ }
+
+ @Test
+ void getAllNotes_WithMultipleNotes() {
+ // Given
+ for (int i = 1; i <= 5; i++) {
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setName("Note " + i);
+ noteRequest.setText("Content for note " + i);
+ noteService.add(noteRequest);
}
- Note result = noteService.find(5L);
+ // When
+ List notes = noteService.getAll();
- assertNotNull(result, "Note should be found");
- assertEquals("Note 5", result.getName());
+ // Then
+ assertNotNull(notes);
+ assertEquals(5, notes.size());
+
+ // Проверяем, что все заметки уникальны по ID
+ long uniqueIds = notes.stream()
+ .map(Note::getUuid)
+ .distinct()
+ .count();
+ assertEquals(5, uniqueIds);
}
-}
+}
\ No newline at end of file
diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties
new file mode 100644
index 0000000..d314aea
--- /dev/null
+++ b/src/test/resources/application-test.properties
@@ -0,0 +1,24 @@
+# Helped by ClaudeAI :3
+
+# ???????? ????????????
+spring.application.name=notes-test
+
+# ?????????? H2 ? ?????? ??? ?????? (??????? ? ????????????)
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+
+# JPA ????????? ??? ??????
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.show-sql=false
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.open-in-view=false
+
+# ????????? ???? ??? ??????
+logging.level.org.springframework=WARN
+logging.level.org.hibernate=WARN
+logging.level.com.zaxxer.hikari=WARN
+
+# ????????? ???????? ???????????????? ??? ??????
+spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfigurationty.servlet.SecurityAutoConfiguration
\ No newline at end of file