new-g
This commit is contained in:
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type BaleTypeHandlers struct {
|
||||
Repo *storage.Repository
|
||||
Repo storage.RepositoryInterface
|
||||
}
|
||||
|
||||
func (h *BaleTypeHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type BaleHandlers struct {
|
||||
Repo *storage.Repository
|
||||
Repo storage.RepositoryInterface
|
||||
MQ *mq.RabbitMQ
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type LineHandlers struct {
|
||||
Repo storage.RepositoryInterface
|
||||
}
|
||||
|
||||
func (h *LineHandlers) RegisterRoutes(g *gin.RouterGroup) {
|
||||
line := g.Group("/line")
|
||||
{
|
||||
line.GET("/stages", h.GetLineStages)
|
||||
line.GET("/stages/:id", h.GetLineStageByID)
|
||||
line.POST("/stages", h.CreateLineStage)
|
||||
line.PUT("/stages/:id", h.UpdateLineStage)
|
||||
line.DELETE("/stages/:id", h.DeleteLineStage)
|
||||
}
|
||||
|
||||
sensors := g.Group("/sensors")
|
||||
{
|
||||
sensors.POST("/readings", h.CreateSensorReading)
|
||||
sensors.GET("/readings/stage/:id", h.GetSensorReadingsByStage)
|
||||
}
|
||||
|
||||
events := g.Group("/events")
|
||||
{
|
||||
events.POST("", h.CreateProductionEvent)
|
||||
events.GET("/stage/:id", h.GetProductionEventsByStage)
|
||||
}
|
||||
|
||||
g.GET("/line/stats", h.GetLineStats)
|
||||
}
|
||||
|
||||
// GetLineStages Получить все этапы линии
|
||||
// @Summary Получить все этапы линии
|
||||
// @Description Возвращает список всех этапов производственной линии
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} storage.LineStage
|
||||
// @Router /line/stages [get]
|
||||
func (h *LineHandlers) GetLineStages(c *gin.Context) {
|
||||
stages, err := h.Repo.GetLineStages(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, stages)
|
||||
}
|
||||
|
||||
// GetLineStageByID Получить этап линии по ID
|
||||
// @Summary Получить этап линии по ID
|
||||
// @Description Возвращает этап линии по ID
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID этапа"
|
||||
// @Success 200 {object} storage.LineStage
|
||||
// @Router /line/stages/{id} [get]
|
||||
func (h *LineHandlers) GetLineStageByID(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
stage, err := h.Repo.GetLineStageByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, stage)
|
||||
}
|
||||
|
||||
// CreateLineStage Создать этап линии
|
||||
// @Summary Создать этап линии
|
||||
// @Description Создаёт новый этап производственной линии
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param stage body storage.LineStage true "Данные этапа"
|
||||
// @Success 201 {object} storage.LineStage
|
||||
// @Router /line/stages [post]
|
||||
func (h *LineHandlers) CreateLineStage(c *gin.Context) {
|
||||
var input storage.LineStage
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
stage, err := h.Repo.CreateLineStage(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, stage)
|
||||
}
|
||||
|
||||
// UpdateLineStage Обновить этап линии
|
||||
// @Summary Обновить этап линии
|
||||
// @Description Обновляет данные этапа линии по ID
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID этапа"
|
||||
// @Param stage body storage.LineStage true "Данные этапа"
|
||||
// @Success 200 {object} storage.LineStage
|
||||
// @Router /line/stages/{id} [put]
|
||||
func (h *LineHandlers) UpdateLineStage(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
var input storage.LineStage
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
stage, err := h.Repo.UpdateLineStage(c.Request.Context(), id, input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, stage)
|
||||
}
|
||||
|
||||
// DeleteLineStage Удалить этап линии
|
||||
// @Summary Удалить этап линии
|
||||
// @Description Удаляет этап линии по ID
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID этапа"
|
||||
// @Success 200 {object} map[string]bool
|
||||
// @Router /line/stages/{id} [delete]
|
||||
func (h *LineHandlers) DeleteLineStage(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
if err := h.Repo.DeleteLineStage(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"deleted": true})
|
||||
}
|
||||
|
||||
// CreateSensorReading Создать показание датчика
|
||||
// @Summary Создать показание датчика
|
||||
// @Description Создаёт новое показание датчика для этапа линии
|
||||
// @Tags sensors
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param reading body storage.SensorReading true "Данные показания"
|
||||
// @Success 201 {object} storage.SensorReading
|
||||
// @Router /sensors/readings [post]
|
||||
func (h *LineHandlers) CreateSensorReading(c *gin.Context) {
|
||||
var input storage.SensorReading
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
reading, err := h.Repo.CreateSensorReading(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, reading)
|
||||
}
|
||||
|
||||
// GetSensorReadingsByStage Получить показания датчиков по этапу
|
||||
// @Summary Получить показания датчиков по этапу
|
||||
// @Description Возвращает последние 100 показаний датчиков для указанного этапа
|
||||
// @Tags sensors
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID этапа"
|
||||
// @Success 200 {array} storage.SensorReading
|
||||
// @Router /sensors/readings/stage/{id} [get]
|
||||
func (h *LineHandlers) GetSensorReadingsByStage(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
readings, err := h.Repo.GetSensorReadingsByStage(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, readings)
|
||||
}
|
||||
|
||||
// CreateProductionEvent Создать событие на линии
|
||||
// @Summary Создать событие на линии
|
||||
// @Description Создаёт новое событие на этапе производственной линии
|
||||
// @Tags events
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param event body storage.ProductionEvent true "Данные события"
|
||||
// @Success 201 {object} storage.ProductionEvent
|
||||
// @Router /events [post]
|
||||
func (h *LineHandlers) CreateProductionEvent(c *gin.Context) {
|
||||
var input storage.ProductionEvent
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
event, err := h.Repo.CreateProductionEvent(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, event)
|
||||
}
|
||||
|
||||
// GetProductionEventsByStage Получить события по этапу
|
||||
// @Summary Получить события по этапу
|
||||
// @Description Возвращает последние 100 событий для указанного этапа
|
||||
// @Tags events
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "ID этапа"
|
||||
// @Success 200 {array} storage.ProductionEvent
|
||||
// @Router /events/stage/{id} [get]
|
||||
func (h *LineHandlers) GetProductionEventsByStage(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
events, err := h.Repo.GetProductionEventsByStage(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, events)
|
||||
}
|
||||
|
||||
// GetLineStats Получить статистику линии
|
||||
// @Summary Получить статистику линии
|
||||
// @Description Возвращает сводную статистику работы производственной линии
|
||||
// @Tags line
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} storage.LineStats
|
||||
// @Router /line/stats [get]
|
||||
func (h *LineHandlers) GetLineStats(c *gin.Context) {
|
||||
stats, err := h.Repo.GetLineStats(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/rostpoliplast/backend/internal/storage"
|
||||
)
|
||||
|
||||
type testRepo struct {
|
||||
baleTypes []storage.BaleType
|
||||
bales []storage.Bale
|
||||
stages []storage.LineStage
|
||||
readings []storage.SensorReading
|
||||
events []storage.ProductionEvent
|
||||
stats *storage.LineStats
|
||||
}
|
||||
|
||||
func (r *testRepo) GetBales(ctx context.Context) ([]storage.Bale, error) {
|
||||
return r.bales, nil
|
||||
}
|
||||
func (r *testRepo) GetBaleByID(ctx context.Context, id string) (*storage.Bale, error) {
|
||||
for _, b := range r.bales {
|
||||
if b.ID == 1 {
|
||||
return &b, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *testRepo) CreateBale(ctx context.Context, input storage.Bale) (*storage.Bale, error) {
|
||||
b := storage.Bale{ID: 1, TypeID: input.TypeID, Timestamp: time.Now()}
|
||||
r.bales = append(r.bales, b)
|
||||
return &b, nil
|
||||
}
|
||||
func (r *testRepo) UpdateBale(ctx context.Context, id string, input storage.Bale) (*storage.Bale, error) {
|
||||
return &input, nil
|
||||
}
|
||||
func (r *testRepo) DeleteBale(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
func (r *testRepo) GetBaleTypeByType(ctx context.Context, typeName string) (*storage.BaleType, error) {
|
||||
for _, bt := range r.baleTypes {
|
||||
if bt.Type == typeName {
|
||||
return &bt, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *testRepo) GetBaleTypes(ctx context.Context) ([]storage.BaleType, error) {
|
||||
return r.baleTypes, nil
|
||||
}
|
||||
func (r *testRepo) GetBaleTypeByID(ctx context.Context, id int) (*storage.BaleType, error) {
|
||||
for _, bt := range r.baleTypes {
|
||||
if bt.ID == id {
|
||||
return &bt, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *testRepo) CreateBaleType(ctx context.Context, input storage.BaleType) (*storage.BaleType, error) {
|
||||
bt := storage.BaleType{ID: 1, Type: input.Type, Weight: input.Weight}
|
||||
r.baleTypes = append(r.baleTypes, bt)
|
||||
return &bt, nil
|
||||
}
|
||||
func (r *testRepo) UpdateBaleType(ctx context.Context, id int, input storage.BaleType) (*storage.BaleType, error) {
|
||||
return &input, nil
|
||||
}
|
||||
func (r *testRepo) DeleteBaleType(ctx context.Context, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *testRepo) GetLineStages(ctx context.Context) ([]storage.LineStage, error) {
|
||||
return r.stages, nil
|
||||
}
|
||||
func (r *testRepo) GetLineStageByID(ctx context.Context, id int) (*storage.LineStage, error) {
|
||||
for _, s := range r.stages {
|
||||
if s.ID == id {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *testRepo) CreateLineStage(ctx context.Context, input storage.LineStage) (*storage.LineStage, error) {
|
||||
s := storage.LineStage{ID: 1, Name: input.Name, Order: input.Order}
|
||||
r.stages = append(r.stages, s)
|
||||
return &s, nil
|
||||
}
|
||||
func (r *testRepo) UpdateLineStage(ctx context.Context, id int, input storage.LineStage) (*storage.LineStage, error) {
|
||||
return &input, nil
|
||||
}
|
||||
func (r *testRepo) DeleteLineStage(ctx context.Context, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *testRepo) CreateSensorReading(ctx context.Context, input storage.SensorReading) (*storage.SensorReading, error) {
|
||||
s := storage.SensorReading{ID: 1, StageID: input.StageID, Value: input.Value}
|
||||
r.readings = append(r.readings, s)
|
||||
return &s, nil
|
||||
}
|
||||
func (r *testRepo) GetSensorReadingsByStage(ctx context.Context, stageID int) ([]storage.SensorReading, error) {
|
||||
var result []storage.SensorReading
|
||||
for _, s := range r.readings {
|
||||
if s.StageID == stageID {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *testRepo) CreateProductionEvent(ctx context.Context, input storage.ProductionEvent) (*storage.ProductionEvent, error) {
|
||||
e := storage.ProductionEvent{ID: 1, StageID: input.StageID, EventType: input.EventType}
|
||||
r.events = append(r.events, e)
|
||||
return &e, nil
|
||||
}
|
||||
func (r *testRepo) GetProductionEventsByStage(ctx context.Context, stageID int) ([]storage.ProductionEvent, error) {
|
||||
var result []storage.ProductionEvent
|
||||
for _, e := range r.events {
|
||||
if e.StageID == stageID {
|
||||
result = append(result, e)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *testRepo) GetLineStats(ctx context.Context) (*storage.LineStats, error) {
|
||||
if r.stats != nil {
|
||||
return r.stats, nil
|
||||
}
|
||||
return &storage.LineStats{TotalItems: 42, RejectedItems: 2, Status: "running"}, nil
|
||||
}
|
||||
|
||||
func setupRouter(repo storage.RepositoryInterface) *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
v1 := r.Group("/api/v1")
|
||||
|
||||
lineH := &LineHandlers{Repo: repo}
|
||||
baleH := &BaleHandlers{Repo: repo, MQ: nil}
|
||||
baleTypeH := &BaleTypeHandlers{Repo: repo}
|
||||
|
||||
lineH.RegisterRoutes(v1)
|
||||
baleH.RegisterRoutes(v1)
|
||||
baleTypeH.RegisterRoutes(v1)
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGetBales(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
bales: []storage.Bale{{ID: 1, TypeID: 1, Type: "test", Timestamp: time.Now()}},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/bales", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
var resp []storage.Bale
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
if len(resp) != 1 {
|
||||
t.Errorf("expected 1 bale, got %d", len(resp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBale(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"typeId": 1}`)
|
||||
req := httptest.NewRequest("POST", "/api/v1/bales", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
}
|
||||
|
||||
var resp storage.Bale
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
if resp.TypeID != 1 {
|
||||
t.Errorf("expected typeId 1, got %d", resp.TypeID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBalesWithTypeQuery(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
baleTypes: []storage.BaleType{{ID: 1, Type: "standard"}},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("POST", "/api/v1/bales?type=standard", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBaleTypes(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
baleTypes: []storage.BaleType{{ID: 1, Type: "PET", Weight: 10.0}},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/bale-types", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
var resp []storage.BaleType
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
if len(resp) != 1 {
|
||||
t.Errorf("expected 1 baleType, got %d", len(resp))
|
||||
}
|
||||
if resp[0].Type != "PET" {
|
||||
t.Errorf("expected type 'PET', got '%s'", resp[0].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBaleType(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"type": "HDPE", "weight": 15.5, "height": 100, "width": 80, "length": 120}`)
|
||||
req := httptest.NewRequest("POST", "/api/v1/bale-types", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLineStages(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
stages: []storage.LineStage{
|
||||
{ID: 1, Name: "Разгрузка", Order: 1, Equipment: "Рампа", MQTTTopic: "/sensor/weight"},
|
||||
{ID: 2, Name: "QR Сортировка", Order: 2, Equipment: "Camera", MQTTTopic: "/qr/result"},
|
||||
},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/line/stages", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
var resp []storage.LineStage
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
if len(resp) != 2 {
|
||||
t.Errorf("expected 2 stages, got %d", len(resp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateLineStage(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"name": "Тестовый этап", "description": "Описание", "order": 99, "equipment": "Оборудование", "mqtt_topic": "/test/topic"}`)
|
||||
req := httptest.NewRequest("POST", "/api/v1/line/stages", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSensorReading(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"stage_id": 1, "sensor": "vibration", "value": 42.5, "unit": "mm/s"}`)
|
||||
req := httptest.NewRequest("POST", "/api/v1/sensors/readings", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSensorReadingsByStage(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
readings: []storage.SensorReading{
|
||||
{ID: 1, StageID: 1, Sensor: "vibration", Value: 42.5, Unit: "mm/s", Timestamp: time.Now()},
|
||||
},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/sensors/readings/stage/1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProductionEvent(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"stage_id": 1, "event_type": "item_processed", "data": "success"}`)
|
||||
req := httptest.NewRequest("POST", "/api/v1/events", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected %d, got %d", http.StatusCreated, w.Code)
|
||||
t.Logf("Response: %s", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProductionEventsByStage(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
events: []storage.ProductionEvent{
|
||||
{ID: 1, StageID: 1, EventType: "item_processed", Data: "success", Timestamp: time.Now()},
|
||||
},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/events/stage/1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLineStats(t *testing.T) {
|
||||
repo := &testRepo{
|
||||
stats: &storage.LineStats{
|
||||
TotalItems: 100,
|
||||
RejectedItems: 5,
|
||||
AvgSpeed: 1.5,
|
||||
CurrentStage: "Экструзия",
|
||||
Status: "running",
|
||||
},
|
||||
}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/line/stats", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
var resp storage.LineStats
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
if resp.TotalItems != 100 {
|
||||
t.Errorf("expected totalItems 100, got %d", resp.TotalItems)
|
||||
}
|
||||
if resp.RejectedItems != 5 {
|
||||
t.Errorf("expected rejectedItems 5, got %d", resp.RejectedItems)
|
||||
}
|
||||
if resp.Status != "running" {
|
||||
t.Errorf("expected status 'running', got '%s'", resp.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateBaleType(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
body := bytes.NewBufferString(`{"type": "UPDATED", "weight": 20.0}`)
|
||||
req := httptest.NewRequest("PUT", "/api/v1/bale-types/1", body)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBaleType(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("DELETE", "/api/v1/bale-types/1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidIDReturns400(t *testing.T) {
|
||||
repo := &testRepo{}
|
||||
r := setupRouter(repo)
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/line/stages/abc", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected %d, got %d", http.StatusBadRequest, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthEndpoint(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
@@ -28,4 +28,9 @@ func (h *Handlers) RegisterRoutes(r *gin.RouterGroup) {
|
||||
MQ: h.MQ,
|
||||
}
|
||||
queueHandlers.RegisterRoutes(r)
|
||||
|
||||
lineHandlers := &LineHandlers{
|
||||
Repo: h.Repo,
|
||||
}
|
||||
lineHandlers.RegisterRoutes(r)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user