Feature: Domain not-found errors on repository writes, image attachment and deletes As the maintainer of RenewCMS I want an update, an image attachment or a delete against an unknown article, user or image id to report a domain not-found error instead of a raw GORM error or a silent no-op So that a caller can rely on errors.Is(err, article.ErrArticleNotFound), errors.Is(err, user.ErrUserNotFound) or errors.Is(err, image.ErrImageNotFound) on every repository method, not just the read side, and so that DELETE /v1/article/{id} reports 404 instead of leaking a 500 for an id that no longer exists Background: Given internal/domain/article defines the sentinel article.ErrArticleNotFound And internal/domain/user defines the sentinel user.ErrUserNotFound And internal/domain/image defines the sentinel image.ErrImageNotFound And ArticleRepository.UpdateBody, UpdateIsOnline, Delete and AddImage return article.ErrArticleNotFound, not gorm.ErrRecordNotFound and not a silent no-op, when the article id does not match any row And ArticleRepository.AddImage returns image.ErrImageNotFound, not gorm.ErrRecordNotFound, when the article exists but the image id does not match any row And UserRepository.UpdateVerificationStatus, UpdatePassword, UpdatePasswordResetCode and Delete return user.ErrUserNotFound, not gorm.ErrRecordNotFound and not a silent no-op, when the user id does not match any row And ImageRepository.Delete returns image.ErrImageNotFound, not gorm.ErrRecordNotFound, when the image id does not match any row And DELETE /v1/article/{id} sits behind the jwtauth Verifier and Authenticator group, and requires a valid jwt cookie And UpdateArticle, AddImage, UpdateUser, DeleteUser, CreateImage and DeleteImage have no handler mounted in api/router.go today, so this slice has no other HTTP-observable surface beyond DELETE /v1/article/{id} Scenario Outline: The article repository reports a missing article as a domain error on a write Given a repository backed by an in-memory SQLite database containing an article titled "Backyard Renovation Update" with id 1 When is called with id 9 Then the result is the error article.ErrArticleNotFound And errors.Is(err, gorm.ErrRecordNotFound) is false Examples: | method | extra | | UpdateBody | and body "New body" | | UpdateIsOnline | and is_online true | | Delete | | Scenario: AddImage reports a missing article as a domain error, not a missing image Given a repository backed by an in-memory SQLite database containing an article with id 1 And the same database containing an image with id 1 When AddImage is called with article id 9 and image id 1 Then the result is the error article.ErrArticleNotFound And errors.Is(err, gorm.ErrRecordNotFound) is false Scenario: AddImage reports a missing image as a domain error once the article exists Given a repository backed by an in-memory SQLite database containing an article with id 1 And the same database containing no image with id 9 When AddImage is called with article id 1 and image id 9 Then the result is the error image.ErrImageNotFound And errors.Is(err, gorm.ErrRecordNotFound) is false Scenario Outline: The user repository reports a missing user as a domain error on a write Given a repository backed by an in-memory SQLite database containing a user with id 1, username "alice" and email "alice@example.com" When is called with id 9 Then the result is the error user.ErrUserNotFound And errors.Is(err, gorm.ErrRecordNotFound) is false Examples: | method | extra | | UpdateVerificationStatus | and is_verified true | | UpdatePassword | and password "newHashedPassword" | | UpdatePasswordResetCode | and code "newHashedResetCode" | | Delete | | Scenario: The image repository reports a missing image as a domain error on delete Given a repository backed by an in-memory SQLite database containing an image with id 1 When Delete is called with id 9 Then the result is the error image.ErrImageNotFound And errors.Is(err, gorm.ErrRecordNotFound) is false Scenario: The test fakes mirror the real repositories on every write not-found path Given the in-memory article, user and image fakes used in test/ When UpdateBody, UpdateIsOnline, Delete or AddImage is called against an id absent from the fake's map Then the fake returns article.ErrArticleNotFound or image.ErrImageNotFound, matching the real repository And when UpdateVerificationStatus, UpdatePassword, UpdatePasswordResetCode or Delete is called against an id absent from the fake's map, the fake returns user.ErrUserNotFound, matching the real repository And none of the fakes construct or return gorm.ErrRecordNotFound anymore Scenario: Deleting an unknown article answers 404, not a silent success or a 500 Given a valid jwt cookie for a user with id 1 And the article repository has no article with id 9999 When the client sends DELETE /v1/article/9999 with that cookie Then the response status is 404 And the response body is the text "Article not found" Scenario: Deleting an existing article still answers 204 Given a valid jwt cookie for a user with id 1 And the article repository contains an article with id 1 When the client sends DELETE /v1/article/1 with that cookie Then the response status is 204 And the article repository no longer contains an article with id 1 Scenario: The delete-article route still requires authentication before existence is even checked Given no jwt cookie is sent And the article repository has no article with id 9999 When the client sends DELETE /v1/article/9999 without a cookie Then the response status is 401