diff --git a/server/backend/news.go b/server/backend/news.go index a5bc0877..fac84b5c 100644 --- a/server/backend/news.go +++ b/server/backend/news.go @@ -2,7 +2,6 @@ package backend import ( "context" - "errors" "fmt" pb "github.com/TUM-Dev/Campus-Backend/server/api/tumdev" "github.com/TUM-Dev/Campus-Backend/server/model" @@ -10,8 +9,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" - "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/gorm" ) func (s *CampusServer) GetNewsSources(ctx context.Context, _ *emptypb.Empty) (newsSources *pb.NewsSourceReply, err error) { @@ -36,26 +33,3 @@ func (s *CampusServer) GetNewsSources(ctx context.Context, _ *emptypb.Empty) (ne } return &pb.NewsSourceReply{Sources: resp}, nil } - -func (s *CampusServer) GetTopNews(ctx context.Context, _ *emptypb.Empty) (*pb.GetTopNewsReply, error) { - if err := s.checkDevice(ctx); err != nil { - return nil, err - } - - var res *model.NewsAlert - err := s.db.Joins("Files").Where("NOW() between `from` and `to`").First(&res).Error - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, status.Error(codes.NotFound, "no currenty active top news") - } else if err != nil { - log.WithError(err).Error("could not GetTopNews") - return nil, status.Error(codes.Internal, "could not GetTopNews") - } - - return &pb.GetTopNewsReply{ - ImageUrl: res.Files.URL.String, - Link: res.Link.String, - Created: timestamppb.New(res.Created), - From: timestamppb.New(res.From), - To: timestamppb.New(res.To), - }, nil -} diff --git a/server/backend/newsAlerts.go b/server/backend/newsAlerts.go new file mode 100644 index 00000000..b9addc25 --- /dev/null +++ b/server/backend/newsAlerts.go @@ -0,0 +1,37 @@ +package backend + +import ( + "context" + "errors" + pb "github.com/TUM-Dev/Campus-Backend/server/api/tumdev" + "github.com/TUM-Dev/Campus-Backend/server/model" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/timestamppb" + "gorm.io/gorm" +) + +func (s *CampusServer) GetTopNews(ctx context.Context, _ *emptypb.Empty) (*pb.GetTopNewsReply, error) { + if err := s.checkDevice(ctx); err != nil { + return nil, err + } + + var res *model.NewsAlert + err := s.db.Joins("Files").Where("NOW() between `from` and `to`").First(&res).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "no current active top news") + } else if err != nil { + log.WithError(err).Error("could not GetTopNews") + return nil, status.Error(codes.Internal, "could not GetTopNews") + } + + return &pb.GetTopNewsReply{ + ImageUrl: res.Files.URL.String, + Link: res.Link.String, + Created: timestamppb.New(res.Created), + From: timestamppb.New(res.From), + To: timestamppb.New(res.To), + }, nil +} diff --git a/server/backend/newsAlerts_test.go b/server/backend/newsAlerts_test.go new file mode 100644 index 00000000..dd11cd30 --- /dev/null +++ b/server/backend/newsAlerts_test.go @@ -0,0 +1,114 @@ +package backend + +import ( + "context" + "database/sql" + "github.com/DATA-DOG/go-sqlmock" + pb "github.com/TUM-Dev/Campus-Backend/server/api/tumdev" + "github.com/TUM-Dev/Campus-Backend/server/model" + "github.com/guregu/null" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "regexp" + "testing" + "time" +) + +type NewsAlertSuite struct { + suite.Suite + DB *gorm.DB + mock sqlmock.Sqlmock + deviceBuf *deviceBuffer +} + +func (s *NewsAlertSuite) SetupSuite() { + var ( + db *sql.DB + err error + ) + + db, s.mock, err = sqlmock.New() + require.NoError(s.T(), err) + + dialector := mysql.New(mysql.Config{ + Conn: db, + DriverName: "mysql", + }) + s.mock.ExpectQuery("SELECT VERSION()"). + WillReturnRows(sqlmock.NewRows([]string{"VERSION()"}).AddRow("10.11.4-MariaDB")) + s.DB, err = gorm.Open(dialector, &gorm.Config{}) + require.NoError(s.T(), err) + + s.deviceBuf = newDeviceBuffer() +} + +const ExpectedGetTopNewsQuery = "SELECT `news_alert`.`news_alert`,`news_alert`.`file`,`news_alert`.`name`,`news_alert`.`link`,`news_alert`.`created`,`news_alert`.`from`,`news_alert`.`to`,`Files`.`file` AS `Files__file`,`Files`.`name` AS `Files__name`,`Files`.`path` AS `Files__path`,`Files`.`downloads` AS `Files__downloads`,`Files`.`url` AS `Files__url`,`Files`.`downloaded` AS `Files__downloaded` FROM `news_alert` LEFT JOIN `files` `Files` ON `news_alert`.`file` = `Files`.`file` WHERE NOW() between `from` and `to` ORDER BY `news_alert`.`news_alert` LIMIT 1" + +func (s *NewsAlertSuite) Test_GetTopNewsOne() { + expectedAlert := model.NewsAlert{ + NewsAlert: 1, + FilesID: 3001, + Files: model.Files{ + File: 3001, + Name: "Tournament_app_02-02.png", + Path: "newsalerts/", + Downloads: 0, + URL: sql.NullString{Valid: false}, + Downloaded: sql.NullBool{Bool: true, Valid: true}, + }, + Name: null.String{NullString: sql.NullString{String: "Exzellenzuniversität", Valid: true}}, + Link: null.String{NullString: sql.NullString{String: "https://tum.de", Valid: true}}, + Created: time.Time.Add(time.Now(), time.Hour*-4), + From: time.Time.Add(time.Now(), time.Hour*-2), + To: time.Time.Add(time.Now(), time.Hour*2), + } + s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)). + WillReturnRows(sqlmock.NewRows([]string{"news_alert", "file", "name", "link", "created", "from", "to", "Files__file", "Files__name", "Files__path", "Files__downloads", "Files__url", "Files__downloaded"}). + AddRow(expectedAlert.NewsAlert, expectedAlert.FilesID, expectedAlert.Name, expectedAlert.Link, expectedAlert.Created, expectedAlert.From, expectedAlert.To, expectedAlert.Files.File, expectedAlert.Files.Name, expectedAlert.Files.Path, expectedAlert.Files.Downloads, expectedAlert.Files.URL, expectedAlert.Files.Downloaded)) + + meta := metadata.MD{} + server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} + response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) + require.NoError(s.T(), err) + require.Equal(s.T(), &pb.GetTopNewsReply{ + ImageUrl: expectedAlert.Files.URL.String, + Link: expectedAlert.Link.String, + Created: timestamppb.New(expectedAlert.Created), + From: timestamppb.New(expectedAlert.From), + To: timestamppb.New(expectedAlert.To), + }, response) +} +func (s *NewsAlertSuite) Test_GetTopNewsNone() { + s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)).WillReturnError(gorm.ErrRecordNotFound) + + meta := metadata.MD{} + server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} + response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) + require.Equal(s.T(), status.Error(codes.NotFound, "no current active top news"), err) + require.Nil(s.T(), response) +} +func (s *NewsAlertSuite) Test_GetTopNewsError() { + s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)).WillReturnError(gorm.ErrInvalidDB) + + meta := metadata.MD{} + server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} + response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) + require.Equal(s.T(), status.Error(codes.Internal, "could not GetTopNews"), err) + require.Nil(s.T(), response) +} + +func (s *NewsAlertSuite) AfterTest(_, _ string) { + require.NoError(s.T(), s.mock.ExpectationsWereMet()) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestNewsAlertSuite(t *testing.T) { + suite.Run(t, new(NewsAlertSuite)) +} diff --git a/server/backend/news_test.go b/server/backend/news_test.go index 39ee5680..c0f175c1 100644 --- a/server/backend/news_test.go +++ b/server/backend/news_test.go @@ -10,15 +10,11 @@ import ( "github.com/guregu/null" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/driver/mysql" "gorm.io/gorm" "regexp" "testing" - "time" ) type NewsSuite struct { @@ -49,21 +45,25 @@ func (s *NewsSuite) SetupSuite() { s.deviceBuf = newDeviceBuffer() } +func file(id int32) *model.Files { + return &model.Files{ + File: id, + Name: fmt.Sprintf("src_%d.png", id), + Path: "news/sources", + Downloads: 1, + URL: sql.NullString{Valid: false}, + Downloaded: sql.NullBool{Bool: true, Valid: true}, + } +} + func source1() *model.NewsSource { return &model.NewsSource{ Source: 1, Title: "Amazing News 1", URL: null.String{NullString: sql.NullString{String: "https://example.com/amazing1", Valid: true}}, - FilesID: 2, - Files: model.Files{ - File: 2, - Name: "src_2.png", - Path: "news/sources", - Downloads: 1, - URL: sql.NullString{Valid: false}, - Downloaded: sql.NullBool{Bool: true, Valid: true}, - }, - Hook: null.String{NullString: sql.NullString{String: "", Valid: true}}, + FilesID: file(2).File, + Files: *file(2), + Hook: null.String{NullString: sql.NullString{String: "", Valid: true}}, } } @@ -72,16 +72,9 @@ func source2() *model.NewsSource { Source: 2, Title: "Amazing News 2", URL: null.String{NullString: sql.NullString{String: "https://example.com/amazing2", Valid: true}}, - FilesID: 2, - Files: model.Files{ - File: 2, - Name: "src_2.png", - Path: "news/sources", - Downloads: 1, - URL: sql.NullString{Valid: false}, - Downloaded: sql.NullBool{Bool: true, Valid: true}, - }, - Hook: null.String{NullString: sql.NullString{String: "hook", Valid: true}}, + FilesID: file(2).File, + Files: *file(2), + Hook: null.String{NullString: sql.NullString{String: "hook", Valid: true}}, } } @@ -120,67 +113,12 @@ func (s *NewsSuite) Test_GetNewsSourcesNone() { require.Equal(s.T(), expectedResp, response) } -const ExpectedGetTopNewsQuery = "SELECT `news_alert`.`news_alert`,`news_alert`.`file`,`news_alert`.`name`,`news_alert`.`link`,`news_alert`.`created`,`news_alert`.`from`,`news_alert`.`to`,`Files`.`file` AS `Files__file`,`Files`.`name` AS `Files__name`,`Files`.`path` AS `Files__path`,`Files`.`downloads` AS `Files__downloads`,`Files`.`url` AS `Files__url`,`Files`.`downloaded` AS `Files__downloaded` FROM `news_alert` LEFT JOIN `files` `Files` ON `news_alert`.`file` = `Files`.`file` WHERE NOW() between `from` and `to` ORDER BY `news_alert`.`news_alert` LIMIT 1" - -func (s *NewsSuite) Test_GetTopNewsOne() { - expectedAlert := model.NewsAlert{ - NewsAlert: 1, - FilesID: 3001, - Files: model.Files{ - File: 3001, - Name: "Tournament_app_02-02.png", - Path: "newsalerts/", - Downloads: 0, - URL: sql.NullString{Valid: false}, - Downloaded: sql.NullBool{Bool: true, Valid: true}, - }, - Name: null.String{NullString: sql.NullString{String: "Exzellenzuniversität", Valid: true}}, - Link: null.String{NullString: sql.NullString{String: "https://tum.de", Valid: true}}, - Created: time.Time.Add(time.Now(), time.Hour*-4), - From: time.Time.Add(time.Now(), time.Hour*-2), - To: time.Time.Add(time.Now(), time.Hour*2), - } - s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)). - WillReturnRows(sqlmock.NewRows([]string{"news_alert", "file", "name", "link", "created", "from", "to", "Files__file", "Files__name", "Files__path", "Files__downloads", "Files__url", "Files__downloaded"}). - AddRow(expectedAlert.NewsAlert, expectedAlert.FilesID, expectedAlert.Name, expectedAlert.Link, expectedAlert.Created, expectedAlert.From, expectedAlert.To, expectedAlert.Files.File, expectedAlert.Files.Name, expectedAlert.Files.Path, expectedAlert.Files.Downloads, expectedAlert.Files.URL, expectedAlert.Files.Downloaded)) - - meta := metadata.MD{} - server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} - response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) - require.NoError(s.T(), err) - require.Equal(s.T(), &pb.GetTopNewsReply{ - ImageUrl: expectedAlert.Files.URL.String, - Link: expectedAlert.Link.String, - Created: timestamppb.New(expectedAlert.Created), - From: timestamppb.New(expectedAlert.From), - To: timestamppb.New(expectedAlert.To), - }, response) -} -func (s *NewsSuite) Test_GetTopNewsNone() { - s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)).WillReturnError(gorm.ErrRecordNotFound) - - meta := metadata.MD{} - server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} - response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) - require.Equal(s.T(), status.Error(codes.NotFound, "no currenty active top news"), err) - require.Nil(s.T(), response) -} -func (s *NewsSuite) Test_GetTopNewsError() { - s.mock.ExpectQuery(regexp.QuoteMeta(ExpectedGetTopNewsQuery)).WillReturnError(gorm.ErrInvalidDB) - - meta := metadata.MD{} - server := CampusServer{db: s.DB, deviceBuf: s.deviceBuf} - response, err := server.GetTopNews(metadata.NewIncomingContext(context.Background(), meta), nil) - require.Equal(s.T(), status.Error(codes.Internal, "could not GetTopNews"), err) - require.Nil(s.T(), response) -} - func (s *NewsSuite) AfterTest(_, _ string) { require.NoError(s.T(), s.mock.ExpectationsWereMet()) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run -func TestExampleTestSuite(t *testing.T) { +func TestNewsSuite(t *testing.T) { suite.Run(t, new(NewsSuite)) }