Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/app/languine.lock
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ files:
tests.dashboard.passed: a0d0628f6b4e4d78d2ffef4d4d1c4b15
tests.dashboard.failed: d7c8c85bf79bbe1b7188497c32c3b0ca
tests.dashboard.severity_distribution: ae8a57ea08a489cace7c1d66e826e112
tests.severity.info: 4059b0251f66a18cb56f544728796875
tests.severity.low: 28d0edd045e05cf5af64e35ae0c4c6ef
tests.severity.medium: 87f8a6ab85c9ced3702b4ea641ad4bb5
tests.severity.high: 655d20c1ca69519ca647684edbb2db35
Expand Down
5 changes: 4 additions & 1 deletion apps/app/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "comp.ai",
"name": "@bubba/app",
"version": "0.1.0",
"private": true,
"scripts": {
Expand Down Expand Up @@ -106,5 +106,8 @@
"overrides": {
"tiptap-extension-global-drag-handle": "^0.1.18"
}
},
"exports": {
"./src/lib/encryption": "./src/lib/encryption.ts"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,105 +9,113 @@ import { unstable_cache } from "next/cache";
import { redirect } from "next/navigation";

export default async function TestsOverview({
params,
params,
}: {
params: Promise<{ locale: string }>;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
setStaticParamsLocale(locale);
const { locale } = await params;
setStaticParamsLocale(locale);

const session = await auth();
const session = await auth();

if (!session?.user?.organizationId) {
redirect("/onboarding");
}
if (!session?.user?.organizationId) {
redirect("/onboarding");
}

const overview = await getTestsOverview(session.user.organizationId);
const organizationId = session.user.organizationId;
const overview = await getTestsOverview(session.user.organizationId);

if (overview?.totalTests === 0) {
redirect(`/${organizationId}/tests/all`);
}
if (overview?.totalTests === 0) {
redirect("/tests/all");
}

return (
<div className="space-y-4 sm:space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<TestsSeverity
totalTests={overview.totalTests}
lowSeverityTests={overview.lowSeverityTests}
mediumSeverityTests={overview.mediumSeverityTests}
highSeverityTests={overview.highSeverityTests}
criticalSeverityTests={overview.criticalSeverityTests}
/>
<TestsByAssignee organizationId={organizationId} />
</div>
</div>
);
return (
<div className="space-y-4 sm:space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<TestsSeverity
totalTests={overview.totalTests}
infoSeverityTests={overview.infoSeverityTests}
lowSeverityTests={overview.lowSeverityTests}
mediumSeverityTests={overview.mediumSeverityTests}
highSeverityTests={overview.highSeverityTests}
criticalSeverityTests={overview.criticalSeverityTests}
/>
<TestsByAssignee organizationId={session.user.organizationId} />
</div>
</div>
);
}

const getTestsOverview = unstable_cache(
async (organizationId: string) => {
return await db.$transaction(async (tx) => {
const [
totalTests,
lowSeverityTests,
mediumSeverityTests,
highSeverityTests,
criticalSeverityTests,
] = await Promise.all([
tx.organizationIntegrationResults.count({
where: {
organizationId,
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "LOW",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "MEDIUM",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "HIGH",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "CRITICAL",
},
}),
]);
async (organizationId: string) => {
return await db.$transaction(async (tx) => {
const [
totalTests,
infoSeverityTests,
lowSeverityTests,
mediumSeverityTests,
highSeverityTests,
criticalSeverityTests,
] = await Promise.all([
tx.organizationIntegrationResults.count({
where: {
organizationId,
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "INFO",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "LOW",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "MEDIUM",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "HIGH",
},
}),
tx.organizationIntegrationResults.count({
where: {
organizationId,
label: "CRITICAL",
},
}),
]);

return {
totalTests,
lowSeverityTests,
mediumSeverityTests,
highSeverityTests,
criticalSeverityTests,
};
});
},
["tests-overview-cache"],
return {
totalTests,
infoSeverityTests,
lowSeverityTests,
mediumSeverityTests,
highSeverityTests,
criticalSeverityTests,
};
});
},
Comment on lines +49 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Assess query performance with indexing.
Counting results by label can be expensive for large data. Ensure that your database schema has appropriate indexes on label to maintain performance at scale. If necessary, create a composite index for (organizationId, label).


🏁 Script executed:

#!/bin/bash
# View schema definitions for organizationIntegrationResults table
rg 'CREATE TABLE' -A 30

Length of output: 86271


Performance Improvement Suggestion: Indexing for Count Queries
It appears that the migration file for the organization integration results (specifically, packages/db/prisma/migrations/20250227185651_add_organization_integration_results/migration.sql) currently creates indexes for the organizationId (and related columns) but does not include an index on the label field. Since the code performs count queries filtering by both organizationId and label (e.g., counting tests with "INFO", "LOW", etc.), this could lead to performance issues on large datasets.

  • Verify the query execution plans for these count operations.
  • Consider adding an index on the label column or a composite index on (organizationId, label) to improve performance and scalability.

["tests-overview-cache"],
);

export async function generateMetadata({
params,
params,
}: {
params: Promise<{ locale: string }>;
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
setStaticParamsLocale(locale);
const t = await getI18n();
const { locale } = await params;
setStaticParamsLocale(locale);
const t = await getI18n();

return {
title: t("sidebar.tests"),
};
return {
title: t("sidebar.tests"),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,22 @@ import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card";
import { useI18n } from "@/locales/client";
import { useTest } from "../../hooks/useTest";
import { Skeleton } from "@bubba/ui/skeleton";
import { AlertCircle, CheckCircle2, Clock, Info, XCircle, User as UserIcon } from "lucide-react";
import { AlertCircle, User as UserIcon } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@bubba/ui/alert";
import { Label } from "@bubba/ui/label";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@bubba/ui/tabs";
import { Badge } from "@bubba/ui/badge";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@bubba/ui/table";
import { AssigneeSection } from "./AssigneeSection";
import { TestComment } from "./TestComments";
import { unstable_cache } from "next/cache";
import { db } from "@bubba/db";
import type { Test } from "../../types";
import type { User } from "@bubba/db/types";

interface CloudTestDetailsProps {
testId: string;
users: User[];
}

const getUsers = unstable_cache(
async (organizationId: string) => {
const users = await db.user.findMany({
where: { organizationId: organizationId },
});

return users;
},
["users-cache"],
);

export function TestDetails({ testId, users }: CloudTestDetailsProps) {
const t = useI18n();
const { cloudTest, isLoading, error, mutate } = useTest(testId);
Expand Down Expand Up @@ -85,9 +73,9 @@ export function TestDetails({ testId, users }: CloudTestDetailsProps) {
// Format the test status for display with appropriate badge color
const getStatusBadge = (status: string) => {
switch(status.toUpperCase()) {
case "ACTIVE":
case "PASSED":
return <Badge className="bg-green-500">{status}</Badge>;
case "DRAFT":
case "IN_PROGRESS":
return <Badge className="bg-yellow-500">{status}</Badge>;
Comment on lines +76 to 79

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify status name changes across the application

You've changed status names from "ACTIVE" to "PASSED" and "DRAFT" to "IN_PROGRESS", which makes them more descriptive. However, ensure these changes are consistent across the application to prevent potential bugs.


🏁 Script executed:

#!/bin/bash
# Check for references to the old status names (ACTIVE, DRAFT) in the codebase
echo "Searching for references to old status names..."
rg -i "('|\")ACTIVE('|\")" --type tsx --type ts
rg -i "('|\")DRAFT('|\")" --type tsx --type ts

Length of output: 267


Action: Update verification script for old status name references

The initial script encountered a file type error due to unrecognized TSX files. To ensure consistency of the status updates (i.e. no lingering references to "ACTIVE" or "DRAFT"), please run the revised script below that adds TSX support:

Once you verify that these searches return no references, you can be confident that the status name changes are consistent across the application.


🏁 Script executed:

#!/bin/bash
# Extend ripgrep to recognize TSX files and search for references to old status names
rg --type-add 'tsx:*.tsx' -i "('|\")ACTIVE('|\")" --type tsx --type ts || echo "No references to 'ACTIVE' found."
rg --type-add 'tsx:*.tsx' -i "('|\")DRAFT('|\")" --type tsx --type ts || echo "No references to 'DRAFT' found."

Length of output: 6207


Action: Ensure Consistent Status Naming Across the Codebase

The test file update in apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/components/TestDetails.tsx now displays the new statuses ("PASSED" and "IN_PROGRESS"). However, our recent search shows that several parts of the codebase still reference the previous status names. For example:

  • Employee Status:
    In apps/app/src/components/tables/people/employee-status.tsx, the constant EMPLOYEE_STATUS_TYPES remains as ["active", "inactive"].

  • Integrations & Triggers:
    Files like packages/integrations/src/gcp/src/index.ts and apps/app/src/trigger/deel/index.ts check against "ACTIVE" (or "active").

  • Policy & Evidence Modules:
    Numerous files under the dashboard (e.g., in policy pages, evidence tables, and related localization strings in apps/app/src/locales/en.ts) still use "draft" instead of "IN_PROGRESS".

Please review these areas and confirm whether the renaming should be propagated application-wide. If a consistent status naming scheme is intended, update the affected files accordingly to prevent potential bugs.

case "FAILED":
return <Badge className="bg-red-500">{status}</Badge>;
Expand All @@ -96,6 +84,11 @@ export function TestDetails({ testId, users }: CloudTestDetailsProps) {
}
};

// Check if resources exist and have items
const hasResources = cloudTest.resultDetails?.Resources && cloudTest.resultDetails.Resources.length > 0;
// Set default tab based on resources availability
const defaultTab = hasResources ? "resources" : "raw-log";

// // Helper function to get the appropriate icon for test run status
// const getRunStatusIcon = (status: string, result: string | null) => {
// if (status === "COMPLETED") {
Expand Down Expand Up @@ -161,18 +154,19 @@ export function TestDetails({ testId, users }: CloudTestDetailsProps) {
</CardContent>
</Card>

<Tabs defaultValue="resources">
<Tabs defaultValue={defaultTab}>
<TabsList>
<TabsTrigger value="resources">Resources</TabsTrigger>
{hasResources && <TabsTrigger value="resources">Resources</TabsTrigger>}
<TabsTrigger value="raw-log">Raw Log</TabsTrigger>
</TabsList>

<TabsContent value="resources" className="mt-6">
<Card>
<CardHeader>
<CardTitle>Resources</CardTitle>
</CardHeader>
<CardContent>
{hasResources && (
<TabsContent value="resources" className="mt-6">
<Card>
<CardHeader>
<CardTitle>Resources</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
Expand All @@ -184,7 +178,7 @@ export function TestDetails({ testId, users }: CloudTestDetailsProps) {
</TableHeader>
<TableBody>
{cloudTest.resultDetails?.Resources.map((resource: any) => (
<TableRow key="{resource.id}">
<TableRow key={resource.Id}>
<TableCell>
<div className="flex items-center gap-2">
{resource.Id}
Expand All @@ -210,9 +204,10 @@ export function TestDetails({ testId, users }: CloudTestDetailsProps) {
))}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
</CardContent>
</Card>
</TabsContent>
)}

<TabsContent value="raw-log" className="mt-6">
<Card>
Expand Down
Loading