@@ -150,12 +150,35 @@ func (r *contactHandlerFakeRepo) snapshot() ([][]*entities.Contact, []*entities.
150150
151151const contactHandlerTestUserID = entities .UserID ("user-id" )
152152
153+ type contactHandlerEntitlementUserRepo struct {
154+ repositories.UserRepository
155+ subscriptionName entities.SubscriptionName
156+ }
157+
158+ func (repository * contactHandlerEntitlementUserRepo ) Load (_ context.Context , userID entities.UserID ) (* entities.User , error ) {
159+ return & entities.User {ID : userID , SubscriptionName : repository .subscriptionName }, nil
160+ }
161+
153162func newContactHandlerTestApp (repo repositories.ContactRepository ) * fiber.App {
163+ return newContactHandlerTestAppWithEntitlements (repo , false , entities .SubscriptionNameFree )
164+ }
165+
166+ func newContactHandlerTestAppWithEntitlements (
167+ repo repositories.ContactRepository ,
168+ entitlementsEnabled bool ,
169+ subscriptionName entities.SubscriptionName ,
170+ ) * fiber.App {
154171 logger := & messageThreadHandlerNoopLogger {}
155172 tracer := telemetry .NewOtelLogger ("test" , logger )
156173 appCache := cache .NewMemoryCache (tracer , ttlCache .New (time .Minute , time .Minute ))
157174 service := services .NewContactService (logger , tracer , repo , appCache )
158- handler := NewContactHandler (logger , tracer , validators .NewContactHandlerValidator (logger , tracer ), service )
175+ entitlementService := services .NewEntitlementService (
176+ logger ,
177+ tracer ,
178+ entitlementsEnabled ,
179+ & contactHandlerEntitlementUserRepo {subscriptionName : subscriptionName },
180+ )
181+ handler := NewContactHandler (logger , tracer , validators .NewContactHandlerValidator (logger , tracer ), service , entitlementService )
159182
160183 app := fiber .New ()
161184 app .Use (func (c fiber.Ctx ) error {
@@ -222,6 +245,41 @@ func TestContactHandler_Store_CreatesManyContactsFromObjectShape(t *testing.T) {
222245 assert .Equal (t , "Bob" , stored [0 ][1 ].Name )
223246}
224247
248+ func TestContactHandler_Store_RejectsBatchExceedingContactLimit (t * testing.T ) {
249+ repo := & contactHandlerFakeRepo {countResult : 199 }
250+ app := newContactHandlerTestAppWithEntitlements (repo , true , entities .SubscriptionNameFree )
251+
252+ body := `{"contacts":[
253+ {"name":"Alice","phone_numbers":["+18005550199"]},
254+ {"name":"Bob","phone_numbers":["+18005550100"]}
255+ ]}`
256+ req := httptest .NewRequest (http .MethodPost , "/v1/contacts" , bytes .NewBufferString (body ))
257+ req .Header .Set ("Content-Type" , "application/json" )
258+
259+ resp , err := app .Test (req , fiber.TestConfig {Timeout : time .Second })
260+ require .NoError (t , err )
261+ require .Equal (t , http .StatusPaymentRequired , resp .StatusCode )
262+
263+ stored , _ , _ , _ := repo .snapshot ()
264+ assert .Empty (t , stored )
265+ }
266+
267+ func TestContactHandler_Store_DisabledEntitlementsDoNotLimitContacts (t * testing.T ) {
268+ repo := & contactHandlerFakeRepo {countResult : 200 }
269+ app := newContactHandlerTestAppWithEntitlements (repo , false , entities .SubscriptionNameFree )
270+
271+ body := `[{"name":"Alice","phone_numbers":["+18005550199"]}]`
272+ req := httptest .NewRequest (http .MethodPost , "/v1/contacts" , bytes .NewBufferString (body ))
273+ req .Header .Set ("Content-Type" , "application/json" )
274+
275+ resp , err := app .Test (req , fiber.TestConfig {Timeout : time .Second })
276+ require .NoError (t , err )
277+ require .Equal (t , http .StatusCreated , resp .StatusCode )
278+
279+ stored , _ , _ , _ := repo .snapshot ()
280+ require .Len (t , stored , 1 )
281+ }
282+
225283func TestContactHandler_Store_ValidationError_ReturnsUnprocessableEntity (t * testing.T ) {
226284 repo := & contactHandlerFakeRepo {}
227285 app := newContactHandlerTestApp (repo )
@@ -289,6 +347,23 @@ func TestContactHandler_Upload_CSVSuccess(t *testing.T) {
289347 }
290348}
291349
350+ func TestContactHandler_Upload_RejectsBatchExceedingContactLimit (t * testing.T ) {
351+ repo := & contactHandlerFakeRepo {countResult : 199 }
352+ app := newContactHandlerTestAppWithEntitlements (repo , true , entities .SubscriptionNameFree )
353+
354+ csv := "Name,Emails,PhoneNumbers\n Alice,alice@example.com,+18005550199\n Bob,,+18005550100\n "
355+ body , contentType := buildContactCSVUpload (t , "contacts.csv" , "text/csv" , csv )
356+ req := httptest .NewRequest (http .MethodPost , "/v1/contacts/upload" , body )
357+ req .Header .Set ("Content-Type" , contentType )
358+
359+ resp , err := app .Test (req , fiber.TestConfig {Timeout : time .Second })
360+ require .NoError (t , err )
361+ require .Equal (t , http .StatusPaymentRequired , resp .StatusCode )
362+
363+ stored , _ , _ , _ := repo .snapshot ()
364+ assert .Empty (t , stored )
365+ }
366+
292367func TestContactHandler_Upload_NonCSVFile_ReturnsUnprocessableEntity (t * testing.T ) {
293368 repo := & contactHandlerFakeRepo {}
294369 app := newContactHandlerTestApp (repo )
0 commit comments