Merge pull request #1 from Tok1shu/db-add-request

Migrated to DB use
This commit is contained in:
Tokishu
2025-05-23 14:29:52 -07:00
committed by GitHub
9 changed files with 319 additions and 108 deletions
+41
View File
@@ -46,6 +46,34 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -54,6 +82,19 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<argLine>
-XX:+EnableDynamicAgentLoading
-Djdk.instrument.traceUsage=false
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -1,11 +1,17 @@
package net.tokishu.note.controller; package net.tokishu.note.controller;
import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note; import net.tokishu.note.model.Note;
import net.tokishu.note.service.NoteService; import net.tokishu.note.service.NoteService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@RestController @RestController
public class NotesController { public class NotesController {
@@ -14,27 +20,30 @@ public class NotesController {
private NoteService noteService; private NoteService noteService;
@GetMapping("/notes") @GetMapping("/notes")
public List<Note> getAll(){ public ResponseEntity<?> getAll(){
return noteService.getAll(); return ResponseEntity.ok(noteService.getAll());
} }
@GetMapping("/note/{id}") @GetMapping("/note/{uuid}")
public Note find(@PathVariable Long id){ public Note find(@PathVariable UUID uuid){
return noteService.find(id); return noteService.find(uuid);
} }
@PostMapping("/note") @PostMapping("/note")
public Note add(@RequestBody Note note){ public ResponseEntity<?> add(@RequestBody NoteRequest note){
return noteService.add(note); noteService.add(note);
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "Note added"));
} }
@PutMapping("/note/{id}") @PutMapping("/note/{uuid}")
public Note update(@PathVariable Long id, @RequestBody Note note){ public ResponseEntity<?> update(@PathVariable("uuid") UUID uuid, @RequestBody NoteRequest note){
return noteService.update(id, note); noteService.update(uuid, note);
return ResponseEntity.ok(Map.of("message", "Note updated"));
} }
@DeleteMapping("/note/{id}") @DeleteMapping("/note/{uuid}")
public void delete(@PathVariable Long id){ public ResponseEntity<?> delete(@PathVariable UUID uuid){
noteService.delete(id); noteService.delete(uuid);
return ResponseEntity.ok(Map.of(" message", "Note deleted"));
} }
} }
@@ -0,0 +1,9 @@
package net.tokishu.note.dto;
import lombok.Data;
@Data
public class NoteRequest {
private String name;
private String text;
}
+12 -40
View File
@@ -1,46 +1,18 @@
package net.tokishu.note.model; 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 { public class Note {
private Long id; @Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID uuid;
private String name; private String name;
private String text; 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 + '\'' +
'}';
}
} }
@@ -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<net.tokishu.note.model.Note, UUID> {
}
@@ -1,52 +1,52 @@
package net.tokishu.note.service; package net.tokishu.note.service;
import lombok.RequiredArgsConstructor;
import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note; import net.tokishu.note.model.Note;
import net.tokishu.note.repo.NoteRepository;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ResponseStatusException;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@Service @Service
@RequiredArgsConstructor
public class NoteService { public class NoteService {
private List<Note> notes = new ArrayList<>();
private AtomicLong idGenerator = new AtomicLong(1); public final NoteRepository noteRepository;
public List<Note> getAll(){ public List<Note> getAll(){
return notes; return noteRepository.findAll();
} }
public Note find(Long id) { public Note find(UUID uuid) {
for (Note note : notes) { return noteRepository.findById(uuid)
if (note.getId().equals(id)) { .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
return note;
}
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found");
} }
public Note add(Note note){ public Note add(NoteRequest data){
note.setId(idGenerator.getAndIncrement()); Note note = new Note();
notes.add(note); note.setName(data.getName());
return note; note.setText(data.getText());
return noteRepository.save(note);
} }
public Note update(Long id, Note updatedNote){ public Note update(UUID uuid, NoteRequest data){
for (Note note : notes){ Note existing = noteRepository.findById(uuid)
if (note.getId().equals(id)) { .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
note.setName(updatedNote.getName()); existing.setName(data.getName());
note.setText(updatedNote.getText()); existing.setText(data.getText());
return note;
} return noteRepository.save(existing);
}
throw new NoSuchElementException("Note not found");
} }
public void delete(Long id){ public void delete(UUID uuid){
notes.removeIf(note -> note.getId().equals(id)); Note existing = noteRepository.findById(uuid)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Note not found"));
noteRepository.deleteById(uuid);
} }
} }
@@ -1,2 +1,11 @@
spring.application.name=notes spring.application.name=notes
spring.mvc.servlet.path=/api 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
@@ -1,63 +1,202 @@
package net.tokishu.note.service; package net.tokishu.note.service;
import net.tokishu.note.dto.NoteRequest;
import net.tokishu.note.model.Note; import net.tokishu.note.model.Note;
import net.tokishu.note.repo.NoteRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; 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.List;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@ActiveProfiles("test") // Используем тестовый профиль
@Transactional // Откатывает транзакции после каждого теста
public class NoteServiceTest { public class NoteServiceTest {
@Autowired
private NoteService noteService; private NoteService noteService;
@Autowired
private NoteRepository noteRepository;
@BeforeEach @BeforeEach
void setUp(){ void setUp() {
noteService = new NoteService(); // Очищаем БД перед каждым тестом
noteRepository.deleteAll();
} }
@Test @Test
void addAndGetAllNotes(){ void addAndGetAllNotes() {
Note note = new Note(1L, "QQ", "Test content"); // Given
noteService.add(note); NoteRequest noteRequest = new NoteRequest();
noteRequest.setName("Test Note");
noteRequest.setText("Test content");
// When
Note savedNote = noteService.add(noteRequest);
List<Note> notes = noteService.getAll(); List<Note> notes = noteService.getAll();
// Then
assertNotNull(savedNote);
assertNotNull(savedNote.getUuid());
assertEquals(1, notes.size()); 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 @Test
void updateNote(){ void findExistingNote() {
Note note = new Note(1L, "Old QQ", "Old test content"); // Given
noteService.add(note); 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"); // When
noteService.update(1L, updatedNote); Note foundNote = noteService.find(savedNote.getUuid());
Note result = noteService.getAll().get(0); // Then
assertEquals("New day, new qq :3", result.getName()); assertNotNull(foundNote);
assertEquals("Find Me", foundNote.getName());
assertEquals("Find content", foundNote.getText());
assertEquals(savedNote.getUuid(), foundNote.getUuid());
} }
@Test @Test
void deleteNote(){ void findNonExistingNote_ShouldThrowException() {
Note noteToDelete = new Note(1L, "Bye bye :(", "content"); // Given
noteService.add(noteToDelete); UUID nonExistingId = UUID.randomUUID();
noteService.delete(1L);
assertTrue(noteService.getAll().isEmpty()); // When & Then
ResponseStatusException exception = assertThrows(
ResponseStatusException.class,
() -> noteService.find(nonExistingId)
);
assertTrue(exception.getMessage().contains("Note not found"));
} }
@Test @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++) { NoteRequest updateRequest = new NoteRequest();
Note note = new Note(i, "Note " + i, "Content for note №" + i); updateRequest.setName("Updated Name");
noteService.add(note); 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());
} }
Note result = noteService.find(5L); @Test
void updateNonExistingNote_ShouldThrowException() {
// Given
UUID nonExistingId = UUID.randomUUID();
NoteRequest updateRequest = new NoteRequest();
updateRequest.setName("Update Name");
updateRequest.setText("Update content");
assertNotNull(result, "Note should be found"); // When & Then
assertEquals("Note 5", result.getName()); 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<Note> 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<Note> 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);
}
// When
List<Note> notes = noteService.getAll();
// Then
assertNotNull(notes);
assertEquals(5, notes.size());
// Проверяем, что все заметки уникальны по ID
long uniqueIds = notes.stream()
.map(Note::getUuid)
.distinct()
.count();
assertEquals(5, uniqueIds);
} }
} }
@@ -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