From 3221a31ac8b0a21808d152c19d241c0f054a22e5 Mon Sep 17 00:00:00 2001 From: Mazyar Date: Sat, 4 Jul 2026 13:56:07 +0330 Subject: [PATCH] Refactor tender service methods for rejected tenders and enhance unit tests - Renamed `buildRejectedTenderIDSetForCompanies` to `buildRejectedTenderIDSetsForCompanies` to better reflect its functionality of returning a map of excluded tender IDs by company. - Updated the implementation to return a nested map structure for better organization of excluded tender IDs per company. - Added a new test function `TestIsRecommendedTenderExcludedForAllCompanies` to validate the visibility of tenders based on company-specific exclusions. - Enhanced existing tests to ensure comprehensive coverage of the new logic for handling rejected tenders. This update improves the clarity and functionality of the tender recommendation process by refining the handling of rejected tenders and expanding test coverage. --- internal/tender/recommendation_filter.go | 31 +++++++++--- internal/tender/recommendation_filter_test.go | 49 +++++++++++++++---- internal/tender/service.go | 13 +++-- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/internal/tender/recommendation_filter.go b/internal/tender/recommendation_filter.go index 162f82c..6426c10 100644 --- a/internal/tender/recommendation_filter.go +++ b/internal/tender/recommendation_filter.go @@ -46,15 +46,15 @@ func (s *tenderService) buildRejectedTenderIDSet(ctx context.Context, companyID return excluded, nil } -func (s *tenderService) buildRejectedTenderIDSetForCompanies(ctx context.Context, companyIDs []string) (map[string]struct{}, error) { - excluded := make(map[string]struct{}) +func (s *tenderService) buildRejectedTenderIDSetsForCompanies(ctx context.Context, companyIDs []string) (map[string]map[string]struct{}, error) { + excludedByCompany := make(map[string]map[string]struct{}) if s.rejectedTenderLister == nil { - return excluded, nil + return excludedByCompany, nil } normalized := normalizeRecommendationCompanyScope(companyIDs) if len(normalized) == 0 { - return excluded, nil + return excludedByCompany, nil } results := make([][]string, len(normalized)) @@ -75,11 +75,13 @@ func (s *tenderService) buildRejectedTenderIDSetForCompanies(ctx context.Context return nil, err } - for _, ids := range results { - addTenderIDsToSet(excluded, ids) + for i, companyID := range normalized { + excluded := make(map[string]struct{}) + addTenderIDsToSet(excluded, results[i]) + excludedByCompany[companyID] = excluded } - return excluded, nil + return excludedByCompany, nil } func addTenderIDsToSet(set map[string]struct{}, ids []string) { @@ -117,3 +119,18 @@ func isRecommendedTenderExcluded(tender Tender, tenderRef string, excluded map[s return false } + +func isRecommendedTenderExcludedForAllCompanies(tender Tender, tenderRef string, excludedByCompany map[string]map[string]struct{}, companyIDs []string) bool { + normalized := normalizeRecommendationCompanyScope(companyIDs) + if len(normalized) == 0 { + return false + } + + for _, companyID := range normalized { + if !isRecommendedTenderExcluded(tender, tenderRef, excludedByCompany[companyID]) { + return false + } + } + + return true +} diff --git a/internal/tender/recommendation_filter_test.go b/internal/tender/recommendation_filter_test.go index 6ce3937..19e26df 100644 --- a/internal/tender/recommendation_filter_test.go +++ b/internal/tender/recommendation_filter_test.go @@ -71,7 +71,7 @@ func TestIsRecommendedTenderExcluded(t *testing.T) { } } -func TestBuildRejectedTenderIDSetForCompanies(t *testing.T) { +func TestBuildRejectedTenderIDSetsForCompanies(t *testing.T) { service := &tenderService{ rejectedTenderLister: stubRejectedTenderLister{ byCompany: map[string][]string{ @@ -81,17 +81,48 @@ func TestBuildRejectedTenderIDSetForCompanies(t *testing.T) { }, } - got, err := service.buildRejectedTenderIDSetForCompanies(context.Background(), []string{"company-a", "company-b", "company-a"}) + got, err := service.buildRejectedTenderIDSetsForCompanies(context.Background(), []string{"company-a", "company-b", "company-a"}) if err != nil { - t.Fatalf("buildRejectedTenderIDSetForCompanies() error = %v", err) + t.Fatalf("buildRejectedTenderIDSetsForCompanies() error = %v", err) } - for _, tenderID := range []string{"tender-1", "tender-2", "tender-3"} { - if _, ok := got[tenderID]; !ok { - t.Fatalf("expected excluded set to contain %q", tenderID) - } + if _, ok := got["company-a"]["tender-1"]; !ok { + t.Fatalf("expected company-a exclusions to contain tender-1") } - if len(got) != 3 { - t.Fatalf("buildRejectedTenderIDSetForCompanies() len = %d, want 3", len(got)) + if _, ok := got["company-a"]["tender-2"]; !ok { + t.Fatalf("expected company-a exclusions to contain tender-2") + } + if _, ok := got["company-b"]["tender-2"]; !ok { + t.Fatalf("expected company-b exclusions to contain tender-2") + } + if _, ok := got["company-b"]["tender-3"]; !ok { + t.Fatalf("expected company-b exclusions to contain tender-3") + } + if len(got) != 2 { + t.Fatalf("buildRejectedTenderIDSetsForCompanies() len = %d, want 2", len(got)) + } +} + +func TestIsRecommendedTenderExcludedForAllCompanies(t *testing.T) { + tender := Tender{ + Model: mongo.Model{}, + ContractFolderID: "055329e0-3d8f-4eeb-946a-85a451f2e36b", + NoticePublicationID: "00423458-2026", + } + tender.ID, _ = bson.ObjectIDFromHex("507f1f77bcf86cd799439011") + + procRef := FormatAIProcedureRef(tender.ContractFolderID, tender.NoticePublicationID) + excludedByCompany := map[string]map[string]struct{}{ + "company-a": {procRef: {}}, + "company-b": {}, + } + + if isRecommendedTenderExcludedForAllCompanies(tender, procRef, excludedByCompany, []string{"company-a", "company-b"}) { + t.Fatalf("expected tender to remain visible when not all companies rejected it") + } + + excludedByCompany["company-b"][procRef] = struct{}{} + if !isRecommendedTenderExcludedForAllCompanies(tender, procRef, excludedByCompany, []string{"company-a", "company-b"}) { + t.Fatalf("expected tender to be hidden when all companies rejected it") } } diff --git a/internal/tender/service.go b/internal/tender/service.go index 25b0494..d499dca 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -973,10 +973,13 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat return nil, err } - var excluded map[string]struct{} + var ( + excluded map[string]struct{} + excludedByCompany map[string]map[string]struct{} + ) if form.ExcludeRejectedTenders { if len(companyIDs) > 0 { - excluded, err = s.buildRejectedTenderIDSetForCompanies(ctx, companyIDs) + excludedByCompany, err = s.buildRejectedTenderIDSetsForCompanies(ctx, companyIDs) } else { excluded, err = s.buildRejectedTenderIDSet(ctx, companyID) } @@ -999,7 +1002,11 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat if !ok { continue } - if isRecommendedTenderExcluded(tender, tenderRef, excluded) { + if len(companyIDs) > 0 { + if isRecommendedTenderExcludedForAllCompanies(tender, tenderRef, excludedByCompany, companyIDs) { + continue + } + } else if isRecommendedTenderExcluded(tender, tenderRef, excluded) { continue } if form.OnlyActiveDeadlines && !tender.IsRecommendable(now) {