Skip to content

Feat/epic 2 workspace team management#13

Merged
LofoWalker merged 9 commits intomainfrom
feat/epic-2-workspace-team-management
Jan 27, 2026
Merged

Feat/epic 2 workspace team management#13
LofoWalker merged 9 commits intomainfrom
feat/epic-2-workspace-team-management

Conversation

@LofoWalker
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings January 27, 2026 21:02
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements Epic 2: company workspaces and team management, including backend domain, persistence, and REST APIs plus corresponding frontend flows for creating and managing company workspaces and invitations. It also introduces basic CI scripts, husky hooks, and updates sprint status documentation.

Changes:

  • Add company, membership, and invitation domain models, repositories, use cases, REST resources, and database migrations to support workspace creation, dashboards, member management, and invitations.
  • Implement frontend company context, pages (company dashboard, workspace creation, team settings, accept invitation), and UI components (select, toast wiring, layout tweaks) wired to the new backend APIs.
  • Introduce husky pre-commit/pre-push hooks, CI helper scripts, and mark Epic 2 stories as done in sprint documentation.

Reviewed changes

Copilot reviewed 84 out of 140 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
package.json Adds prepare script for husky and CI helper scripts for web and API builds/tests.
package-lock.json Locks new dependencies (husky, @radix-ui/react-select and related packages).
docs/implementation-artifacts/sprint-status.yaml Marks Epic 2 and all its stories as done.
apps/web/vite.config.js Minor import formatting; no functional change.
apps/web/tailwind.config.ts Minor import formatting; no functional change.
apps/web/src/pages/TeamSettingsPage.tsx New Team Settings page for listing members, inviting users, and changing roles via the company APIs.
apps/web/src/pages/CreateCompanyPage.tsx New page hosting CreateCompanyForm inside the onboarding layout for workspace creation.
apps/web/src/pages/CompanyDashboardPage.tsx New company dashboard shell showing member count and onboarding “Get Started” steps; redirects users without a company to the create-company flow using hasFetchedCompanies.
apps/web/src/pages/AcceptInvitationPage.tsx Public page to view invitation details and accept/decline; contains a bug where the login redirect uses a redirect query param that the current login flow ignores.
apps/web/src/lib/utils.ts Minor import formatting; no functional change.
apps/web/src/hooks/use-toast.ts Minor import formatting; continues to expose toast hook used by new pages.
apps/web/src/features/company/index.ts Barrel file exporting CompanyProvider, useCompany, CreateCompanyForm, and company API functions.
apps/web/src/features/company/api.ts Frontend API client/types for companies, dashboards, members, invitations, and roles; aligns shapes with new backend responses.
apps/web/src/features/company/CreateCompanyForm.tsx Form for creating a company workspace with slug generation, server-side field error handling, and navigation to /dashboard on success.
apps/web/src/features/company/CompanyContext.tsx React context for company state (companies list, current company, dashboard, loading/error states, hasFetchedCompanies flag) and methods to refresh or create companies.
apps/web/src/features/auth/LoginForm.tsx Reorders React imports; login behavior (including post-login redirect via location.state.from) unchanged.
apps/web/src/features/auth/AuthContext.tsx Improves token-refresh path by fetching fresh user data after refresh and updating localStorage.
apps/web/src/components/ui/toaster.tsx Simplifies imports; continues to render toasts based on useToast.
apps/web/src/components/ui/toast.tsx Import formatting only; toast behavior unchanged.
apps/web/src/components/ui/separator.tsx Import formatting only.
apps/web/src/components/ui/select.tsx New Radix-based Select UI component with trigger, content, items, and scroll buttons; used in role selection in Team Settings.
apps/web/src/components/ui/label.tsx Import formatting only.
apps/web/src/components/ui/input.tsx Import formatting only.
apps/web/src/components/ui/input.stories.tsx Import formatting only.
apps/web/src/components/ui/index.ts Re-exports useToast and the new Select components through the UI barrel file.
apps/web/src/components/ui/form-input.tsx Import formatting only.
apps/web/src/components/ui/dropdown-menu.tsx Import formatting only.
apps/web/src/components/ui/dropdown-menu.stories.tsx Import formatting only.
apps/web/src/components/ui/dialog.tsx Import formatting only.
apps/web/src/components/ui/dialog.stories.tsx Import formatting only.
apps/web/src/components/ui/card.tsx Import formatting only.
apps/web/src/components/ui/card.stories.tsx Import formatting only.
apps/web/src/components/ui/button.tsx Import formatting only.
apps/web/src/components/ui/button.stories.tsx Import formatting only.
apps/web/src/components/ui/badge.tsx Import formatting only.
apps/web/src/components/ui/badge.stories.tsx Import formatting only.
apps/web/src/components/ui/avatar.tsx Import formatting only.
apps/web/src/components/ui/avatar.stories.tsx Import formatting only.
apps/web/src/components/ui/alert.tsx Import formatting only.
apps/web/src/components/ui/alert.stories.tsx Import formatting only.
apps/web/src/components/layout/WorkspaceSwitcher.tsx Import formatting only; workspace switcher remains wired to company list.
apps/web/src/components/layout/WorkspaceSwitcher.stories.tsx Import formatting only.
apps/web/src/components/layout/UserMenu.tsx Import formatting and hook path update; user menu logic unchanged.
apps/web/src/components/layout/TabNav.tsx Import formatting only.
apps/web/src/components/layout/TabNav.stories.tsx Import formatting only.
apps/web/src/components/layout/PublicPageLayout.tsx Import formatting only.
apps/web/src/components/layout/PublicHeader.tsx Import formatting only.
apps/web/src/components/layout/ProgressStepper.tsx Import formatting only.
apps/web/src/components/layout/ProgressStepper.stories.tsx Import formatting only.
apps/web/src/components/layout/PageLoading.tsx Import formatting only.
apps/web/src/components/layout/PageLoading.stories.tsx Import formatting only.
apps/web/src/components/layout/PageError.tsx Import formatting only.
apps/web/src/components/layout/PageError.stories.tsx Import formatting only.
apps/web/src/components/layout/OnboardingLayout.tsx Import formatting only; used by the new create-company page.
apps/web/src/components/layout/Navbar.tsx Import formatting only.
apps/web/src/components/layout/Logo.tsx Import formatting only.
apps/web/src/components/layout/Footer.tsx Import formatting only.
apps/web/src/components/layout/DashboardLayout.tsx Import formatting; continues to host navbar, optional tabs, and main content for dashboard-like pages.
apps/web/src/components/layout/AdminLayout.tsx Import formatting only.
apps/web/src/components/common/LoadingSpinner.tsx Import formatting only.
apps/web/src/components/common/LoadingSpinner.stories.tsx Import formatting only.
apps/web/src/components/common/ErrorBoundary.tsx Import formatting only.
apps/web/src/App.tsx Wires new routes for company creation, company dashboard, team settings, and invitation acceptance; wraps routes in AuthProvider and CompanyProvider.
apps/web/playwright.config.ts Import formatting only.
apps/web/package.json Adds @radix-ui/react-select dependency for the new select component.
apps/web/e2e/pages/index.ts Import formatting only.
apps/web/e2e/fixtures/index.ts Import formatting only.
apps/web/e2e/app.spec.ts Import formatting only.
apps/web/.storybook/preview.ts Import formatting only.
apps/web/.storybook/main.ts Import formatting only.
apps/api/src/test/java/com/upkeep/application/usecase/CreateCompanyUseCaseImplTest.java New unit tests for CreateCompanyUseCaseImpl covering success, slug generation, slug conflict, name length, and owner role assignment.
apps/api/src/main/resources/db/migration/V5__create_companies_and_memberships_tables.sql Adds companies and memberships tables plus indexes and uniqueness constraints to support workspaces and memberships.
apps/api/src/main/resources/db/migration/V6__create_invitations_table.sql Adds invitations table and indexes for tokens, email/status, and company/email/status.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/membership/MembershipMapper.java Maps between Membership domain model and MembershipEntity.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/membership/MembershipJpaRepository.java Panache-based implementation of MembershipRepository for saving, querying, and deleting memberships.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/membership/MembershipEntity.java JPA entity mapping for the memberships table.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/invitation/InvitationMapper.java Maps between Invitation domain model and InvitationEntity.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/invitation/InvitationJpaRepository.java Panache-based implementation of InvitationRepository.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/invitation/InvitationEntity.java JPA entity mapping for the invitations table.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/company/CompanyMapper.java Maps between Company domain model and CompanyEntity.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/company/CompanyJpaRepository.java Panache-based implementation of CompanyRepository.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/persistence/company/CompanyEntity.java JPA entity mapping for the companies table.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/out/email/MockEmailService.java Extends mock email service to log invitation emails with acceptance links.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/invitation/InvitationResource.java REST resource for GET/accept invitation endpoints using the new invitation use cases and cookie-based auth for acceptance.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/invitation/InvitationDetailsResponse.java DTO for invitation details returned by the GET invitation endpoint.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/invitation/AcceptInvitationResponse.java DTO for the result of accepting an invitation.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/UpdateMemberRoleRequest.java Request DTO with validation for role updates in the member management endpoint.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/MemberResponse.java DTO for individual member records in the company members list.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/InviteUserRequest.java Request DTO with email/role validation for inviting a user to a company.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/InvitationResponse.java DTO for invitations created via the company invitation endpoint.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/CreateCompanyRequest.java Request DTO for creating companies with bean validation mirroring domain constraints.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/CompanyResponse.java DTO for create-company responses, including the creator’s membership.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/CompanyResource.java REST resource exposing company CRUD-like operations, dashboard, invitations, members list, and role updates, all using cookie-based auth.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/CompanyListResponse.java DTO for listing companies associated with the current user.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/company/CompanyDashboardResponse.java DTO for the company dashboard, including user role and basic stats.
apps/api/src/main/java/com/upkeep/infrastructure/adapter/in/rest/common/exception/GlobalExceptionMapper.java Extends domain exception mapping to cover company, membership, invitation, and authorization-related errors with specific codes and HTTP statuses.
apps/api/src/main/java/com/upkeep/domain/model/membership/Role.java Defines membership roles (OWNER, MEMBER) shared across use cases and APIs.
apps/api/src/main/java/com/upkeep/domain/model/membership/MembershipId.java Strongly-typed ID wrapper for memberships.
apps/api/src/main/java/com/upkeep/domain/model/membership/Membership.java Domain model for company membership, including role changes and timestamps.
apps/api/src/main/java/com/upkeep/domain/model/invitation/InvitationToken.java Value object for invitation tokens; generates secure random, URL-safe tokens.
apps/api/src/main/java/com/upkeep/domain/model/invitation/InvitationStatus.java Domain enum for invitation states (PENDING, ACCEPTED, DECLINED, EXPIRED).
apps/api/src/main/java/com/upkeep/domain/model/invitation/InvitationId.java Strongly-typed ID wrapper for invitations.
apps/api/src/main/java/com/upkeep/domain/model/invitation/Invitation.java Domain model for invitations, including creation, expiration, acceptance, and status transitions.
apps/api/src/main/java/com/upkeep/domain/model/company/CompanySlug.java Value object for company slugs; enforces format and length, but currently throws IllegalArgumentException causing 500s for some invalid inputs.
apps/api/src/main/java/com/upkeep/domain/model/company/CompanyName.java Value object for company names; enforces length but uses IllegalArgumentException instead of domain validation.
apps/api/src/main/java/com/upkeep/domain/model/company/CompanyId.java Strongly-typed ID wrapper for companies.
apps/api/src/main/java/com/upkeep/domain/model/company/Company.java Domain model for a company, with ID, name, slug, and timestamps.
apps/api/src/main/java/com/upkeep/domain/exception/UnauthorizedOperationException.java Domain exception for forbidden operations (e.g., non-owners changing roles or inviting).
apps/api/src/main/java/com/upkeep/domain/exception/MembershipNotFoundException.java Domain exception representing a missing membership for a given customer and company.
apps/api/src/main/java/com/upkeep/domain/exception/LastOwnerException.java Domain exception thrown when attempting to remove the last owner from a company.
apps/api/src/main/java/com/upkeep/domain/exception/InvitationNotFoundException.java Domain exception for unknown invitation tokens.
apps/api/src/main/java/com/upkeep/domain/exception/InvitationExpiredException.java Domain exception for expired invitations.
apps/api/src/main/java/com/upkeep/domain/exception/InvitationAlreadyExistsException.java Domain exception for conflicting pending invitations for the same email.
apps/api/src/main/java/com/upkeep/domain/exception/CompanySlugAlreadyExistsException.java Domain exception for slug uniqueness violations.
apps/api/src/main/java/com/upkeep/domain/exception/CompanyNotFoundException.java Domain exception for missing companies.
apps/api/src/main/java/com/upkeep/domain/exception/AlreadyMemberException.java Domain exception raised when accepting an invitation but already a member.
apps/api/src/main/java/com/upkeep/application/usecase/UpdateMemberRoleUseCaseImpl.java Use case to change member roles with ownership checks and last-owner protection; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/InviteUserToCompanyUseCaseImpl.java Use case to invite users to a company with ownership and duplicate-invitation checks; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/GetUserCompaniesUseCaseImpl.java Use case to list companies for a user via memberships; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/GetInvitationUseCaseImpl.java Use case to load invitation details (including company name/status); currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/GetCompanyMembersUseCaseImpl.java Use case to list members of a company after verifying caller membership; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/GetCompanyDashboardUseCaseImpl.java Use case to build a basic company dashboard view and stats; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/usecase/CreateCompanyUseCaseImpl.java Use case to create a company and assign the creator as owner; covered by the new tests.
apps/api/src/main/java/com/upkeep/application/usecase/AcceptInvitationUseCaseImpl.java Use case to accept invitations, handle expiration, and create memberships; currently lacks unit tests.
apps/api/src/main/java/com/upkeep/application/port/out/notification/EmailService.java Port extended with sendInvitationEmail for invitation notifications.
apps/api/src/main/java/com/upkeep/application/port/out/membership/MembershipRepository.java Port definition for membership persistence operations.
apps/api/src/main/java/com/upkeep/application/port/out/invitation/InvitationRepository.java Port definition for invitation persistence operations.
apps/api/src/main/java/com/upkeep/application/port/out/company/CompanyRepository.java Port definition for company persistence operations.
apps/api/src/main/java/com/upkeep/application/port/in/UpdateMemberRoleUseCase.java Port for the update-member-role use case with command/result records.
apps/api/src/main/java/com/upkeep/application/port/in/InviteUserToCompanyUseCase.java Port for the invite-user use case with command/result records.
apps/api/src/main/java/com/upkeep/application/port/in/GetUserCompaniesUseCase.java Port for fetching companies for a user.
apps/api/src/main/java/com/upkeep/application/port/in/GetInvitationUseCase.java Port for fetching invitation details by token.
apps/api/src/main/java/com/upkeep/application/port/in/GetCompanyMembersUseCase.java Port for fetching members of a company.
apps/api/src/main/java/com/upkeep/application/port/in/GetCompanyDashboardUseCase.java Port for fetching a company dashboard view.
apps/api/src/main/java/com/upkeep/application/port/in/CreateCompanyUseCase.java Port for the create-company use case.
apps/api/src/main/java/com/upkeep/application/port/in/AcceptInvitationUseCase.java Port for accepting an invitation.
Copilot-Processing.md Internal notes summarizing analysis and implementation steps for Epic 2; no runtime impact.
.husky/pre-push New Git hook to run web lint/build and API checkstyle/tests/build before allowing pushes.
.husky/pre-commit New Git hook to run a quick web lint before committing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 101 out of 157 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +246
@Path("/api/companies")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CompanyResource {

private static final String ACCESS_TOKEN_COOKIE = "access_token";

private final CreateCompanyUseCase createCompanyUseCase;
private final GetUserCompaniesUseCase getUserCompaniesUseCase;
private final GetCompanyDashboardUseCase getCompanyDashboardUseCase;
private final InviteUserToCompanyUseCase inviteUserToCompanyUseCase;
private final GetCompanyMembersUseCase getCompanyMembersUseCase;
private final UpdateMemberRoleUseCase updateMemberRoleUseCase;
private final TokenService tokenService;

public CompanyResource(CreateCompanyUseCase createCompanyUseCase,
GetUserCompaniesUseCase getUserCompaniesUseCase,
GetCompanyDashboardUseCase getCompanyDashboardUseCase,
InviteUserToCompanyUseCase inviteUserToCompanyUseCase,
GetCompanyMembersUseCase getCompanyMembersUseCase,
UpdateMemberRoleUseCase updateMemberRoleUseCase,
TokenService tokenService) {
this.createCompanyUseCase = createCompanyUseCase;
this.getUserCompaniesUseCase = getUserCompaniesUseCase;
this.getCompanyDashboardUseCase = getCompanyDashboardUseCase;
this.inviteUserToCompanyUseCase = inviteUserToCompanyUseCase;
this.getCompanyMembersUseCase = getCompanyMembersUseCase;
this.updateMemberRoleUseCase = updateMemberRoleUseCase;
this.tokenService = tokenService;
}

@POST
public Response createCompany(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken,
@Valid CreateCompanyRequest request) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

CreateCompanyResult result = createCompanyUseCase.execute(
new CreateCompanyCommand(
claims.userId(),
request.name(),
request.slug()
)
);

CompanyResponse response = new CompanyResponse(
result.companyId(),
result.name(),
result.slug(),
new CompanyResponse.MembershipResponse(
result.membership().membershipId(),
result.membership().role()
)
);

return Response.status(201)
.entity(ApiResponse.success(response))
.build();
}

@GET
public Response getUserCompanies(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

List<CompanyWithMembership> companies = getUserCompaniesUseCase.execute(
new GetUserCompaniesQuery(claims.userId())
);

List<CompanyListResponse> response = companies.stream()
.map(c -> new CompanyListResponse(c.companyId(), c.name(), c.slug(), c.role()))
.toList();

return Response.ok(ApiResponse.success(response)).build();
}

@GET
@Path("/{companyId}/dashboard")
public Response getCompanyDashboard(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken,
@PathParam("companyId") String companyId) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

CompanyDashboard dashboard = getCompanyDashboardUseCase.execute(
new GetCompanyDashboardQuery(claims.userId(), companyId)
);

CompanyDashboardResponse response = new CompanyDashboardResponse(
dashboard.companyId(),
dashboard.name(),
dashboard.slug(),
dashboard.userRole(),
new CompanyDashboardResponse.StatsResponse(
dashboard.stats().totalMembers(),
dashboard.stats().hasBudget(),
dashboard.stats().hasPackages(),
dashboard.stats().hasAllocations()
)
);

return Response.ok(ApiResponse.success(response)).build();
}

@POST
@Path("/{companyId}/invitations")
public Response inviteUser(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken,
@PathParam("companyId") String companyId,
@Valid InviteUserRequest request) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

InviteResult result = inviteUserToCompanyUseCase.execute(
new InviteCommand(
claims.userId(),
companyId,
request.email(),
request.role()
)
);

InvitationResponse response = new InvitationResponse(
result.invitationId(),
result.email(),
result.role(),
result.status(),
result.expiresAt()
);

return Response.status(201)
.entity(ApiResponse.success(response))
.build();
}

@GET
@Path("/{companyId}/members")
public Response getCompanyMembers(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken,
@PathParam("companyId") String companyId) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

List<MemberInfo> members = getCompanyMembersUseCase.execute(
new GetCompanyMembersQuery(claims.userId(), companyId)
);

List<MemberResponse> response = members.stream()
.map(m -> new MemberResponse(
m.membershipId(),
m.customerId(),
m.email(),
m.role(),
m.joinedAt()
))
.toList();

return Response.ok(ApiResponse.success(response)).build();
}

@PATCH
@Path("/{companyId}/members/{membershipId}")
public Response updateMemberRole(@CookieParam(ACCESS_TOKEN_COOKIE) String accessToken,
@PathParam("companyId") String companyId,
@PathParam("membershipId") String membershipId,
@Valid UpdateMemberRoleRequest request) {
TokenClaims claims = validateToken(accessToken);
if (claims == null) {
return unauthorizedResponse();
}

UpdateMemberRoleResult result = updateMemberRoleUseCase.execute(
new UpdateMemberRoleCommand(
claims.userId(),
companyId,
membershipId,
request.role()
)
);

return Response.ok(ApiResponse.success(result)).build();
}

private TokenClaims validateToken(String accessToken) {
if (accessToken == null || accessToken.isBlank()) {
return null;
}
try {
return tokenService.validateAccessToken(accessToken);
} catch (Exception e) {
return null;
}
}

private Response unauthorizedResponse() {
return Response.status(401)
.entity(ApiResponse.error(new ApiError(
"UNAUTHORIZED", "Authentication required", null, null)))
.build();
}
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new company REST endpoints expose multiple behaviors (creating companies, listing user companies, dashboard stats, inviting members, listing members, updating roles) but there are no corresponding integration tests for this resource, while other REST resources (e.g. AuthResource, see apps/api/src/test/java/com/upkeep/infrastructure/adapter/in/rest/auth/AuthResourceTest.java) are covered. To keep API behavior stable and prevent regressions, please add Quarkus REST tests for these endpoints (happy paths and key error cases like unauthorized, forbidden, and domain exceptions).

Copilot uses AI. Check for mistakes.
@LofoWalker LofoWalker merged commit 90dd272 into main Jan 27, 2026
3 checks passed
@LofoWalker LofoWalker deleted the feat/epic-2-workspace-team-management branch February 12, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants