From 040f5de9396b4141990a68d576f31e7e8fb4810c Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 11:41:34 +0500 Subject: [PATCH 01/24] Integrated Paypal --- src/app.ts | 201 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 165 insertions(+), 36 deletions(-) diff --git a/src/app.ts b/src/app.ts index 8a3f89d..edbbbbf 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,15 +2,17 @@ import { ApiError, CheckoutPaymentIntent, Client, + CustomError, Environment, LogLevel, OrdersController, } from '@paypal/paypal-server-sdk'; +// Initialize the PayPal client const client = new Client({ clientCredentialsAuthCredentials: { - oAuthClientId: 'OAuthClientId', - oAuthClientSecret: 'OAuthClientSecret' + oAuthClientId: 'AcYwRJcoVwp5oNkwPbIHGxISi6uVcR1elF7TBlf_hJMZUhOdDZEUy6F4GOHIPcD1PNgJbSW7EIHZsRcx', + oAuthClientSecret: 'EHgRtJ9wG0ZXrrhhVPS6gHl9S54VkpZurQH-PWXEPTC912dsrwjP4itL1KEp5VBe1hDwe5aLl3DlpRcD' }, timeout: 0, environment: Environment.Sandbox, @@ -25,53 +27,180 @@ const client = new Client({ }, }); -const ordersController = new OrdersController(client); +export const ordersController = new OrdersController(client); -async function createAndCaptureOrder() { - const createOrderRequest = { - body: { - intent: CheckoutPaymentIntent.Capture, - purchaseUnits: [ - { - amount: { - currencyCode: 'EUR', - value: '100.00', - }, - } - ], - paymentSource: { - mybank: { - name: 'John Doe', - countryCode: 'IT' - } +/** + * Creates an order + * @param amount - The order amount + * @param currencyCode - The currency code (default: USD) + * @returns Order creation response + */ +export async function createOrder(amount: string = '100.00', currencyCode: string = 'USD') { + try { + const createOrderRequest = { + body: { + intent: CheckoutPaymentIntent.Capture, + purchaseUnits: [ + { + amount: { + currencyCode, + value: amount, + }, + } + ], + }, + prefer: 'return=representation' as const + }; + + const createOrderResponse = await ordersController.createOrder(createOrderRequest); + return createOrderResponse.result; + } catch (error) { + if (error instanceof ApiError) { + console.error('Error creating order:', error.statusCode, error.body); + if (error instanceof CustomError) { + console.error('Custom Error:', error.result?.name, error.result?.message); } - }, - prefer: 'return=minimal' - }; + } else { + console.error('Unexpected Error creating order:', error); + } + throw error; + } +} + +/** + * Retrieves order details by order ID + * @param orderId - The PayPal order ID + * @returns Order details + */ +export async function getOrder(orderId: string) { + try { + const getOrderRequest = { + id: orderId, + // Optional: specify fields to return, or omit for full order details + }; + + const orderResponse = await ordersController.getOrder(getOrderRequest); + return orderResponse.result; + } catch (error) { + if (error instanceof ApiError) { + console.error('Error retrieving order:', error.statusCode, error.body); + if (error instanceof CustomError) { + console.error('Custom Error:', error.result?.name, error.result?.message); + } + } else { + console.error('Unexpected Error retrieving order:', error); + } + throw error; + } +} +export async function createAndCaptureOrder() { try { + // Create an order + const createOrderRequest = { + body: { + intent: CheckoutPaymentIntent.Capture, + purchaseUnits: [ + { + amount: { + currencyCode: 'USD', + value: '100.00', + }, + } + ], + }, + prefer: 'return=representation' // Get full response to access links + }; + const createOrderResponse = await ordersController.createOrder(createOrderRequest); const orderId = createOrderResponse.result.id; + + if (!orderId) { + throw new Error('Order ID is missing from create order response'); + } + + console.log('Order Created:', orderId); + console.log('Order Status:', createOrderResponse.result.status); - console.log('Order created with ID:', orderId); + // Get order details using the order ID + console.log('\nšŸ“‹ Retrieving order details...'); + const orderDetails = await getOrder(orderId); + console.log('āœ… Order Retrieved Successfully!'); + console.log('Order ID:', orderDetails.id); + console.log('Order Status:', orderDetails.status); + console.log('Intent:', orderDetails.intent); + if (orderDetails.purchaseUnits && orderDetails.purchaseUnits.length > 0) { + const purchaseUnit = orderDetails.purchaseUnits[0]; + if (purchaseUnit) { + console.log('Amount:', purchaseUnit.amount?.value, purchaseUnit.amount?.currencyCode); + console.log('Reference ID:', purchaseUnit.referenceId); + } + } + if (orderDetails.createTime) { + console.log('Created At:', orderDetails.createTime); + } + if (orderDetails.updateTime) { + console.log('Updated At:', orderDetails.updateTime); + } + if (orderDetails.links && orderDetails.links.length > 0) { + console.log('Available Links:'); + orderDetails.links.forEach(link => { + console.log(` - ${link.rel}: ${link.href} (${link.method})`); + }); + } - const captureOrderRequest = { - id: orderId as string, - prefer: 'return=minimal' - }; + // Check for approve URL in the response links + const approveLink = createOrderResponse.result.links?.find( + link => link.rel === 'approve' + ); + + if (approveLink) { + console.log('\nāš ļø Order requires payer approval.'); + console.log('Approve URL:', approveLink.href); + console.log('\nFor automated testing, providing payment_source in capture request...\n'); + } - const captureOrderResponse = await ordersController.captureOrder(captureOrderRequest); + // // Capture the order with payment_source for automated testing + // // Using a PayPal sandbox test card for server-side testing + // const captureOrderRequest = { + // id: orderId, + // body: { + // paymentSource: { + // card: { + // number: '4032034814971974', // PayPal sandbox test card + // expiry: '2025-12', // Future expiry date + // securityCode: '123', // CVV + // name: 'Test User' + // } + // } + // }, + // prefer: 'return=representation' + // }; - console.log('Order captured with ID:', captureOrderResponse.result.id); - console.log('Capture status:', captureOrderResponse.result.status); + // const captureOrderResponse = await ordersController.captureOrder(captureOrderRequest); + // console.log('āœ… Order Captured Successfully!'); + // console.log('Capture ID:', captureOrderResponse.result.id); + // console.log('Capture Status:', captureOrderResponse.result.status); + + // if (captureOrderResponse.result.purchaseUnits?.[0]?.payments?.captures?.[0]) { + // const capture = captureOrderResponse.result.purchaseUnits[0].payments.captures[0]; + // console.log('Capture Amount:', capture.amount?.value, capture.amount?.currencyCode); + // } } catch (error) { if (error instanceof ApiError) { - console.error('API Error:', error.statusCode, error.body); + console.error('\nāŒ API Error:', error.statusCode); + console.error('Error Body:', JSON.stringify(error.body, null, 2)); + if (error instanceof CustomError) { + console.error('Error Name:', error.result?.name); + console.error('Error Message:', error.result?.message); + if (error.result?.details) { + console.error('Error Details:', error.result.details); + } + } } else { - console.error('Unexpected Error:', error); + console.error('āŒ Unexpected Error:', error); } + throw error; // Re-throw to allow caller to handle } -} - -createAndCaptureOrder(); \ No newline at end of file +} \ No newline at end of file From 36f6378bc536230bd1bd1c7b677d0cfa8b43ccaf Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 15:42:07 +0500 Subject: [PATCH 02/24] sonar metric changes --- tools/score.ts | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/tools/score.ts b/tools/score.ts index c96b4e8..ddf8775 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -84,19 +84,28 @@ function readSonarMetrics(sonarJson: any) { function normFromSonarMeasure(measures: Record) { const norms: Partial> = {}; - norms.correctness = measures.coverage ? Math.round(Number(measures.coverage)) : 0; - const vulns = Number(measures.vulnerabilities ?? 0); - norms.security = Math.max(0, 100 - vulns * 25); + // const vulns = Number(measures.vulnerabilities ?? 0); + // norms.security = Math.max(0, 100 - vulns * 25); + console.log("[Sonar] code_smells:", measures.code_smells); const smells = Number(measures.code_smells ?? 0); norms.maintainability = Math.max(0, 100 - smells * 0.2); + + console.log("[Sonar] sqale_index (technical_debt):", measures.sqale_index); + console.log("[Sonar] complexity:", measures.complexity); + const complexity = Number(measures.complexity ?? 0); + norms.performance = Math.max(Math.max(0, 100 - complexity)); + + console.log("[Sonar] duplicated_lines_density:", measures.duplicated_lines_density); const dup = Number(measures.duplicated_lines_density ?? 0); norms.duplication = Math.max(0, Math.round(100 - dup)); + + console.log("[Sonar] bugs:", measures.bugs); + console.log("[Sonar] reliability_remediation_effort:", measures.reliability_remediation_effort); - const complexity = Number(measures.complexity ?? 0); - norms.maintainability = Math.round(Math.max(0, Math.min(100, (norms.maintainability ?? 100) - complexity * 0.05))); + // reliability: -1, return norms as Record; } @@ -124,14 +133,20 @@ function main() { // norms from individual tools const norms: Norms = { - correctness: normCoverage(coverage), + // Correctness + unitTestPassRate: -1, + compilation:0, + autofixSuccessRate:0, + // Efficiency + tokenusage:0, + timeToFirstWorkingSolution:0, + fixAttempts:0, + // Quality security: normSemgrep(semgrepJson), - maintainability: normComplexity(escomplexJson), - readability: normESLint(eslintJson, totalLines), - robustness: 90, // placeholder - duplication: 95, - performance: 85, - consistency: 90, + reliability: -1, + maintainability: -1, + duplication:-1, + performance: -1 }; // incorporate Sonar measures From 6df70ab798bcf35d6522f1ec2e371bd09d3191c5 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 16:00:44 +0500 Subject: [PATCH 03/24] scoring changes --- .../ai-code-quality-sonarcloud-manual.yml | 8 ++- tools/score.ts | 65 +++++++++++++------ 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index a550eff..cacf009 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -88,7 +88,9 @@ jobs: env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - METRICS="coverage,security_rating,vulnerabilities,code_smells,duplicated_lines_density,complexity,alert_status,reliability_rating,maintainability_rating" + # Metrics include: coverage, security_rating, vulnerabilities, code_smells, duplicated_lines_density, + # complexity (cyclomatic complexity), alert_status, reliability_rating, maintainability_rating + METRICS="coverage,security_rating,vulnerabilities,code_smells,duplicated_lines_density,complexity,alert_status,reliability_rating,sqale_index" curl -s -u "${SONAR_TOKEN}:" "https://sonarcloud.io/api/measures/component?component=${{ secrets.SONAR_PROJECT_KEY }}&metricKeys=${METRICS}" -o sonar_metrics.json || true echo "Saved sonar_metrics.json" @@ -125,3 +127,7 @@ jobs: echo "Detailed Category Breakdown:" cat score_breakdown.json fi + if [ -f score_report.json ]; then + echo "Complete Score Card:" + cat score_report.json + fi diff --git a/tools/score.ts b/tools/score.ts index ddf8775..41e7c9b 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -135,17 +135,17 @@ function main() { const norms: Norms = { // Correctness unitTestPassRate: -1, - compilation:0, - autofixSuccessRate:0, + compilation: 0, + autofixSuccessRate: 0, // Efficiency - tokenusage:0, - timeToFirstWorkingSolution:0, - fixAttempts:0, + tokenusage: 0, + timeToFirstWorkingSolution: 0, + fixAttempts: 0, // Quality security: normSemgrep(semgrepJson), reliability: -1, maintainability: -1, - duplication:-1, + duplication: -1, performance: -1 }; @@ -159,31 +159,54 @@ function main() { } } - const weights: Weights = { - correctness: 25, - security: 20, - maintainability: 15, - readability: 10, - robustness: 10, - duplication: 6, - performance: 6, - consistency: 8 + const categoryWeights: Record = { + correctness: { + unitTestPassRate: 40, + compilation: 30, + autofixSuccessRate: 30 + }, + quality: { + security: 25, + reliability: 20, + maintainability: 20, + duplication: 20, + performance: 15 + }, + efficiency: { + tokenusage: 35, + timeToFirstWorkingSolution: 35, + fixAttempts: 30 + } }; - const score = computeComposite(norms, weights); + const categoryScores: Record = {}; + for (const [category, weights] of Object.entries(categoryWeights)) { + categoryScores[category] = computeComposite(norms, weights); + } + + const categoryCount = Object.keys(categoryWeights).length || 1; + const score = Math.round( + (Object.values(categoryScores).reduce((sum, val) => sum + val, 0) / categoryCount) * 100 + ) / 100; // Write outputs fs.writeFileSync("composite_score.txt", String(score), "utf8"); // Detailed per-category breakdown for workflow artifact - const breakdown: Record = {}; - for (const k of Object.keys(weights)) { - breakdown[k] = norms[k] ?? 0; - } + const breakdown = { + metrics: norms, + categories: categoryScores + }; fs.writeFileSync("score_breakdown.json", JSON.stringify(breakdown, null, 2), "utf8"); // Full report for debugging/history - const fullReport = { score, norms, weights, timestamp: new Date().toISOString() }; + const fullReport = { + score, + norms, + categoryWeights, + categoryScores, + timestamp: new Date().toISOString() + }; fs.writeFileSync("score_report.json", JSON.stringify(fullReport, null, 2), "utf8"); console.log("Composite Score:", score); From d71953ea8cb204c315e231369c87a8fe55a6c228 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 16:07:20 +0500 Subject: [PATCH 04/24] uploaded score card --- .github/workflows/ai-code-quality-sonarcloud-manual.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index cacf009..631c4ad 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -117,6 +117,7 @@ jobs: path: | composite_score.txt score_breakdown.json + score_report.json - name: Print composite + detailed breakdown run: | From 26d0cf8703ab295b90d3f310184ea4cbb4322ae2 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 16:15:35 +0500 Subject: [PATCH 05/24] soanr metrics printing --- .github/workflows/ai-code-quality-sonarcloud-manual.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index 631c4ad..2a56a04 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -118,6 +118,7 @@ jobs: composite_score.txt score_breakdown.json score_report.json + sonar_metrics.json - name: Print composite + detailed breakdown run: | @@ -132,3 +133,7 @@ jobs: echo "Complete Score Card:" cat score_report.json fi + if [ -f sonar_metrics.json ]; then + echo "sonar metrics:" + cat sonar_metrics.json + fi From 4d6b3a2cd2cde0a2bc554dba15122555a462eb6c Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 16:35:42 +0500 Subject: [PATCH 06/24] reliability & vulnerability added --- .../ai-code-quality-sonarcloud-manual.yml | 5 +++++ tools/score.ts | 17 +++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index 2a56a04..b4db954 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -119,6 +119,7 @@ jobs: score_breakdown.json score_report.json sonar_metrics.json + semgrep.json - name: Print composite + detailed breakdown run: | @@ -137,3 +138,7 @@ jobs: echo "sonar metrics:" cat sonar_metrics.json fi + if [ -f semgrep.json ]; then + echo "semgrep metrics:" + cat semgrep.json + fi diff --git a/tools/score.ts b/tools/score.ts index 41e7c9b..3945050 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -85,27 +85,24 @@ function readSonarMetrics(sonarJson: any) { function normFromSonarMeasure(measures: Record) { const norms: Partial> = {}; - // const vulns = Number(measures.vulnerabilities ?? 0); - // norms.security = Math.max(0, 100 - vulns * 25); - console.log("[Sonar] code_smells:", measures.code_smells); const smells = Number(measures.code_smells ?? 0); - norms.maintainability = Math.max(0, 100 - smells * 0.2); - - console.log("[Sonar] sqale_index (technical_debt):", measures.sqale_index); + norms.maintainability = Math.max(0, 100 - (smells * 0.2) - (measures.sqale_index * 0.2)); + // code_smells: # + // sqale_index: mins console.log("[Sonar] complexity:", measures.complexity); const complexity = Number(measures.complexity ?? 0); norms.performance = Math.max(Math.max(0, 100 - complexity)); + // complexity: mins console.log("[Sonar] duplicated_lines_density:", measures.duplicated_lines_density); const dup = Number(measures.duplicated_lines_density ?? 0); norms.duplication = Math.max(0, Math.round(100 - dup)); + // duplicated_lines_density: % - console.log("[Sonar] bugs:", measures.bugs); - console.log("[Sonar] reliability_remediation_effort:", measures.reliability_remediation_effort); - - // reliability: -1, + norms.reliability = Math.max(0, 100 - (measures.bugs * 0.2)); + // bugs: # return norms as Record; } From e306da226c69545cdf2f877941f517b200c90ecf Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 16:50:17 +0500 Subject: [PATCH 07/24] security implemented --- src/app.ts | 14 ++++++++++++++ tools/score.ts | 19 +++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/app.ts b/src/app.ts index edbbbbf..982abbd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -8,6 +8,20 @@ import { OrdersController, } from '@paypal/paypal-server-sdk'; + +// // insecure-sql.ts +// import mysql from "mysql"; +// const connection = mysql.createConnection({ /* ... */ }); + +// function getUserById(userId: string) { +// // userId comes from request params — DO NOT concatenate directly +// const query = `SELECT * FROM users WHERE id = ${userId}`; // <-- injectable +// connection.query(query, (err, results) => { +// console.log(results); +// }); +// } + + // Initialize the PayPal client const client = new Client({ clientCredentialsAuthCredentials: { diff --git a/tools/score.ts b/tools/score.ts index 3945050..24b60cc 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -101,8 +101,11 @@ function normFromSonarMeasure(measures: Record) { norms.duplication = Math.max(0, Math.round(100 - dup)); // duplicated_lines_density: % - norms.reliability = Math.max(0, 100 - (measures.bugs * 0.2)); - // bugs: # + norms.reliability = Math.max(0, 100 - (measures.reliability_rating * 0.2)); + // reliability_rating: 1 (best) - 5 (worst) + + norms.security = Math.max(0, 100 - (measures.security_rating * 0.2)); + // security_rating: 1 (best) - 5 (worst) return norms as Record; } @@ -132,14 +135,14 @@ function main() { const norms: Norms = { // Correctness unitTestPassRate: -1, - compilation: 0, - autofixSuccessRate: 0, + compilation: -1, + autofixSuccessRate: -1, // Efficiency - tokenusage: 0, - timeToFirstWorkingSolution: 0, - fixAttempts: 0, + tokenusage: -1, + timeToFirstWorkingSolution: -1, + fixAttempts: -1, // Quality - security: normSemgrep(semgrepJson), + security: -1, reliability: -1, maintainability: -1, duplication: -1, From 634fbbe96516c3cc029d33bc9bdafeef048000d0 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 17:02:10 +0500 Subject: [PATCH 08/24] limiting tests to app.ts --- .github/workflows/ai-code-quality-sonarcloud-manual.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index b4db954..66b88d2 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -80,6 +80,7 @@ jobs: -Dsonar.organization=${{ secrets.SONAR_ORG }} -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }} -Dsonar.sources=src + -Dsonar.inclusions=src/app.ts -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From a0bf22ef9f8b966a7ea86c52d23e8ff30aa0290e Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Fri, 28 Nov 2025 17:25:37 +0500 Subject: [PATCH 09/24] Cursor Report Card added --- tools/AgentScoreCard.json | 18 ++++++++++++++++++ tools/score.ts | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tools/AgentScoreCard.json diff --git a/tools/AgentScoreCard.json b/tools/AgentScoreCard.json new file mode 100644 index 0000000..0ecd014 --- /dev/null +++ b/tools/AgentScoreCard.json @@ -0,0 +1,18 @@ +{ + "autofixSuccessRate": 69, + "tokenUsage": 69, + "timeToFirstWorkingSolution": 69, + "fixAttempts": 69, + "examples": { + "autofixSuccessRate": 0.75, + "tokenUsage": 2750, + "timeToFirstWorkingSolution": 375, + "fixAttempts": 3 + }, + "example_explanations": { + "autofixSuccessRate": "Issues resolved Ć· total issues encountered (e.g. 3 resolved out of 4 = 0.75). Range: 0.0–1.0", + "tokenUsage": "Sum of input and output tokens for the change request (integer). Example: 1800 + 950 = 2750", + "timeToFirstWorkingSolution": "Elapsed seconds from user request to working fix. Example: 00:06:15 -> 375 (integer seconds)", + "fixAttempts": "Count of distinct implementation attempts before success (integer). Example: 2 retries = 3 attempts" + } +} \ No newline at end of file diff --git a/tools/score.ts b/tools/score.ts index 24b60cc..391b9fd 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -3,6 +3,13 @@ import fs from "fs"; type Norms = Record; type Weights = Record; +type AgentScoreCard = { + autofixSuccessRate?: number; + tokenUsage?: number; + timeToFirstWorkingSolution?: number; + fixAttempts?: number; + [key: string]: any; +}; function safeRead(p: string) { try { @@ -14,6 +21,26 @@ function safeRead(p: string) { } } +function applyAgentScoreCard(norms: Norms) { + const scoreCard = safeRead("tools/AgentScoreCard.json") as AgentScoreCard | null; + if (!scoreCard) return; + + const fieldMap: Record = { + autofixSuccessRate: "autofixSuccessRate", + tokenusage: "tokenUsage", + timeToFirstWorkingSolution: "timeToFirstWorkingSolution", + fixAttempts: "fixAttempts" + }; + + for (const [normKey, cardKey] of Object.entries(fieldMap)) { + const raw = scoreCard[cardKey]; + const val = typeof raw === "number" ? raw : Number(raw); + if (!Number.isNaN(val)) { + norms[normKey] = val; + } + } +} + /* Normalizers */ // coverage from coverage-summary.json (jest json-summary) @@ -86,17 +113,21 @@ function normFromSonarMeasure(measures: Record) { const norms: Partial> = {}; console.log("[Sonar] code_smells:", measures.code_smells); + console.log("[Sonar] sqale_index:", measures.sqale_index); + console.log("[Sonar] complexity:", measures.complexity); + console.log("[Sonar] duplicated_lines_density:", measures.duplicated_lines_density); + console.log("[Sonar] reliability_rating:", measures.reliability_rating); + console.log("[Sonar] security_rating:", measures.security_rating); + const smells = Number(measures.code_smells ?? 0); norms.maintainability = Math.max(0, 100 - (smells * 0.2) - (measures.sqale_index * 0.2)); // code_smells: # // sqale_index: mins - console.log("[Sonar] complexity:", measures.complexity); const complexity = Number(measures.complexity ?? 0); norms.performance = Math.max(Math.max(0, 100 - complexity)); // complexity: mins - console.log("[Sonar] duplicated_lines_density:", measures.duplicated_lines_density); const dup = Number(measures.duplicated_lines_density ?? 0); norms.duplication = Math.max(0, Math.round(100 - dup)); // duplicated_lines_density: % @@ -149,6 +180,8 @@ function main() { performance: -1 }; + applyAgentScoreCard(norms); + // incorporate Sonar measures if (sonarMetricsRaw) { const sonarMap = readSonarMetrics(sonarMetricsRaw); From 704818d126453e8945927b159bb3ac7038006894 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 12:31:07 +0500 Subject: [PATCH 10/24] cursor report added --- src/app.ts | 323 +++++++++++++++++--------------------- tools/AgentScoreCard.json | 21 ++- tools/score.ts | 76 ++------- 3 files changed, 163 insertions(+), 257 deletions(-) diff --git a/src/app.ts b/src/app.ts index 982abbd..1eddcf8 100644 --- a/src/app.ts +++ b/src/app.ts @@ -8,213 +8,170 @@ import { OrdersController, } from '@paypal/paypal-server-sdk'; +let cachedOrdersController: OrdersController | null = null; -// // insecure-sql.ts -// import mysql from "mysql"; -// const connection = mysql.createConnection({ /* ... */ }); - -// function getUserById(userId: string) { -// // userId comes from request params — DO NOT concatenate directly -// const query = `SELECT * FROM users WHERE id = ${userId}`; // <-- injectable -// connection.query(query, (err, results) => { -// console.log(results); -// }); -// } - - -// Initialize the PayPal client -const client = new Client({ - clientCredentialsAuthCredentials: { - oAuthClientId: 'AcYwRJcoVwp5oNkwPbIHGxISi6uVcR1elF7TBlf_hJMZUhOdDZEUy6F4GOHIPcD1PNgJbSW7EIHZsRcx', - oAuthClientSecret: 'EHgRtJ9wG0ZXrrhhVPS6gHl9S54VkpZurQH-PWXEPTC912dsrwjP4itL1KEp5VBe1hDwe5aLl3DlpRcD' - }, - timeout: 0, - environment: Environment.Sandbox, - logging: { - logLevel: LogLevel.Info, - logRequest: { - logBody: true +function resolveEnvironment(rawValue: string | undefined): Environment { + const normalized = (rawValue ?? 'sandbox').trim().toLowerCase(); + + if (normalized === 'production' || normalized === 'live') { + return Environment.Production; + } + + return Environment.Sandbox; +} + +function parseTimeout(rawTimeout: string | undefined): number { + const parsed = Number.parseInt(rawTimeout ?? '0', 10); + return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0; +} + +function resolveConfig(): { + clientId: string; + clientSecret: string; + environment: Environment; + timeout: number; +} { + const clientId = process.env.PAYPAL_CLIENT_ID; + const clientSecret = process.env.PAYPAL_CLIENT_SECRET; + + if (!clientId || !clientSecret) { + throw new Error( + 'Missing PayPal credentials. Set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables before calling PayPal APIs.', + ); + } + + return { + clientId, + clientSecret, + environment: resolveEnvironment(process.env.PAYPAL_ENV), + timeout: parseTimeout(process.env.PAYPAL_TIMEOUT_MS), + }; +} + +function getOrdersController(): OrdersController { + if (cachedOrdersController) { + return cachedOrdersController; + } + + const { clientId, clientSecret, environment, timeout } = resolveConfig(); + + const client = new Client({ + clientCredentialsAuthCredentials: { + oAuthClientId: clientId, + oAuthClientSecret: clientSecret, }, - logResponse: { - logHeaders: true + environment, + timeout, + logging: { + logLevel: LogLevel.Info, + logRequest: { logBody: true }, + logResponse: { logHeaders: true }, + }, + }); + + cachedOrdersController = new OrdersController(client); + return cachedOrdersController; +} + +function handlePayPalError(context: string, error: unknown): never { + if (error instanceof ApiError) { + console.error(`${context} (status: ${error.statusCode})`); + + if (error.body) { + console.error('Response body:', JSON.stringify(error.body, null, 2)); + } + + if (error instanceof CustomError && error.result) { + console.error('Error name:', error.result.name); + console.error('Error message:', error.result.message); + + if (error.result.details) { + console.error('Error details:', JSON.stringify(error.result.details, null, 2)); + } } - }, -}); - -export const ordersController = new OrdersController(client); - -/** - * Creates an order - * @param amount - The order amount - * @param currencyCode - The currency code (default: USD) - * @returns Order creation response - */ -export async function createOrder(amount: string = '100.00', currencyCode: string = 'USD') { + } else { + console.error(`${context}:`, error); + } + + throw error instanceof Error ? error : new Error(String(error)); +} + +export interface CreateOrderOptions { + amount?: string; + currencyCode?: string; + intent?: CheckoutPaymentIntent; + referenceId?: string; + preferMinimalResponse?: boolean; +} + +export async function createOrder( + options: CreateOrderOptions = {}, +): Promise { + const { + amount = '100.00', + currencyCode = 'USD', + intent = CheckoutPaymentIntent.Capture, + referenceId = 'default', + preferMinimalResponse = false, + } = options; + try { - const createOrderRequest = { + const controller = getOrdersController(); + + const createResponse = await controller.createOrder({ body: { - intent: CheckoutPaymentIntent.Capture, + intent, purchaseUnits: [ { + referenceId, amount: { currencyCode, value: amount, }, - } + }, ], }, - prefer: 'return=representation' as const - }; + prefer: preferMinimalResponse ? 'return=minimal' : 'return=representation', + }); - const createOrderResponse = await ordersController.createOrder(createOrderRequest); - return createOrderResponse.result; + return createResponse.result; } catch (error) { - if (error instanceof ApiError) { - console.error('Error creating order:', error.statusCode, error.body); - if (error instanceof CustomError) { - console.error('Custom Error:', error.result?.name, error.result?.message); - } - } else { - console.error('Unexpected Error creating order:', error); - } - throw error; + return handlePayPalError('Failed to create order', error); } } -/** - * Retrieves order details by order ID - * @param orderId - The PayPal order ID - * @returns Order details - */ -export async function getOrder(orderId: string) { - try { - const getOrderRequest = { - id: orderId, - // Optional: specify fields to return, or omit for full order details - }; +export async function getOrder(orderId: string): Promise { + if (!orderId) { + throw new Error('orderId is required to retrieve an order.'); + } - const orderResponse = await ordersController.getOrder(getOrderRequest); + try { + const controller = getOrdersController(); + const orderResponse = await controller.getOrder({ id: orderId }); return orderResponse.result; } catch (error) { - if (error instanceof ApiError) { - console.error('Error retrieving order:', error.statusCode, error.body); - if (error instanceof CustomError) { - console.error('Custom Error:', error.result?.name, error.result?.message); - } - } else { - console.error('Unexpected Error retrieving order:', error); - } - throw error; + return handlePayPalError('Failed to retrieve order', error); } } -export async function createAndCaptureOrder() { - try { - // Create an order - const createOrderRequest = { - body: { - intent: CheckoutPaymentIntent.Capture, - purchaseUnits: [ - { - amount: { - currencyCode: 'USD', - value: '100.00', - }, - } - ], - }, - prefer: 'return=representation' // Get full response to access links - }; - - const createOrderResponse = await ordersController.createOrder(createOrderRequest); - const orderId = createOrderResponse.result.id; - - if (!orderId) { - throw new Error('Order ID is missing from create order response'); - } - - console.log('Order Created:', orderId); - console.log('Order Status:', createOrderResponse.result.status); - - // Get order details using the order ID - console.log('\nšŸ“‹ Retrieving order details...'); - const orderDetails = await getOrder(orderId); - console.log('āœ… Order Retrieved Successfully!'); - console.log('Order ID:', orderDetails.id); - console.log('Order Status:', orderDetails.status); - console.log('Intent:', orderDetails.intent); - if (orderDetails.purchaseUnits && orderDetails.purchaseUnits.length > 0) { - const purchaseUnit = orderDetails.purchaseUnits[0]; - if (purchaseUnit) { - console.log('Amount:', purchaseUnit.amount?.value, purchaseUnit.amount?.currencyCode); - console.log('Reference ID:', purchaseUnit.referenceId); - } - } - if (orderDetails.createTime) { - console.log('Created At:', orderDetails.createTime); - } - if (orderDetails.updateTime) { - console.log('Updated At:', orderDetails.updateTime); - } - if (orderDetails.links && orderDetails.links.length > 0) { - console.log('Available Links:'); - orderDetails.links.forEach(link => { - console.log(` - ${link.rel}: ${link.href} (${link.method})`); - }); - } - - // Check for approve URL in the response links - const approveLink = createOrderResponse.result.links?.find( - link => link.rel === 'approve' - ); - - if (approveLink) { - console.log('\nāš ļø Order requires payer approval.'); - console.log('Approve URL:', approveLink.href); - console.log('\nFor automated testing, providing payment_source in capture request...\n'); - } +async function runSample(): Promise { + if ((process.env.RUN_PAYPAL_ORDER_SAMPLE ?? '').toLowerCase() !== 'true') { + return; + } - // // Capture the order with payment_source for automated testing - // // Using a PayPal sandbox test card for server-side testing - // const captureOrderRequest = { - // id: orderId, - // body: { - // paymentSource: { - // card: { - // number: '4032034814971974', // PayPal sandbox test card - // expiry: '2025-12', // Future expiry date - // securityCode: '123', // CVV - // name: 'Test User' - // } - // } - // }, - // prefer: 'return=representation' - // }; - - // const captureOrderResponse = await ordersController.captureOrder(captureOrderRequest); - // console.log('āœ… Order Captured Successfully!'); - // console.log('Capture ID:', captureOrderResponse.result.id); - // console.log('Capture Status:', captureOrderResponse.result.status); - - // if (captureOrderResponse.result.purchaseUnits?.[0]?.payments?.captures?.[0]) { - // const capture = captureOrderResponse.result.purchaseUnits[0].payments.captures[0]; - // console.log('Capture Amount:', capture.amount?.value, capture.amount?.currencyCode); - // } + const createdOrder: any = await createOrder(); - } catch (error) { - if (error instanceof ApiError) { - console.error('\nāŒ API Error:', error.statusCode); - console.error('Error Body:', JSON.stringify(error.body, null, 2)); - if (error instanceof CustomError) { - console.error('Error Name:', error.result?.name); - console.error('Error Message:', error.result?.message); - if (error.result?.details) { - console.error('Error Details:', error.result.details); - } - } - } else { - console.error('āŒ Unexpected Error:', error); - } - throw error; // Re-throw to allow caller to handle + if (!createdOrder.id) { + console.warn('Created order is missing an ID. Skipping retrieval step.'); + return; } -} \ No newline at end of file + + const orderDetails: any = await getOrder(createdOrder.id); + + console.log('Created order status:', createdOrder.status); + console.log('Retrieved order status:', orderDetails.status); +} + +void runSample(); + + diff --git a/tools/AgentScoreCard.json b/tools/AgentScoreCard.json index 0ecd014..92709a9 100644 --- a/tools/AgentScoreCard.json +++ b/tools/AgentScoreCard.json @@ -1,18 +1,15 @@ { - "autofixSuccessRate": 69, - "tokenUsage": 69, - "timeToFirstWorkingSolution": 69, - "fixAttempts": 69, + "issuesFound": 1, + "issuesFixed": 1, + "fixAttempts": 1, "examples": { - "autofixSuccessRate": 0.75, - "tokenUsage": 2750, - "timeToFirstWorkingSolution": 375, - "fixAttempts": 3 + "fixAttempts": 3, + "issuesFound": 2, + "issuesFixed": 1 }, "example_explanations": { - "autofixSuccessRate": "Issues resolved Ć· total issues encountered (e.g. 3 resolved out of 4 = 0.75). Range: 0.0–1.0", - "tokenUsage": "Sum of input and output tokens for the change request (integer). Example: 1800 + 950 = 2750", - "timeToFirstWorkingSolution": "Elapsed seconds from user request to working fix. Example: 00:06:15 -> 375 (integer seconds)", - "fixAttempts": "Count of distinct implementation attempts before success (integer). Example: 2 retries = 3 attempts" + "issuesFound": "Count of issues found in the codebase (integer). Example: 2 issues found during your implementation", + "issuesFixed": "Count of issues fixed in the codebase (integer). Example: 1 issue fixed during your implementation", + "fixAttempts": "Count of distinct implementation attempts before success (integer). Example: 2 retries = 3 attempts during your implementation" } } \ No newline at end of file diff --git a/tools/score.ts b/tools/score.ts index 391b9fd..000d798 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -4,10 +4,9 @@ import fs from "fs"; type Norms = Record; type Weights = Record; type AgentScoreCard = { - autofixSuccessRate?: number; - tokenUsage?: number; - timeToFirstWorkingSolution?: number; fixAttempts?: number; + issuesFound?: number; + issuesFixed?: number; [key: string]: any; }; @@ -26,9 +25,8 @@ function applyAgentScoreCard(norms: Norms) { if (!scoreCard) return; const fieldMap: Record = { - autofixSuccessRate: "autofixSuccessRate", - tokenusage: "tokenUsage", - timeToFirstWorkingSolution: "timeToFirstWorkingSolution", + issuesFound: "issuesFound", + issuesFixed: "issuesFixed", fixAttempts: "fixAttempts" }; @@ -152,7 +150,7 @@ function computeComposite(norms: Norms, weights: Weights): number { return Math.round(s * 100) / 100; } -function main() { +function main(): Norms { const coverage = safeRead("coverage/coverage-summary.json"); const eslintJson = safeRead("eslint.json"); const semgrepJson = safeRead("semgrep.json"); @@ -163,14 +161,14 @@ function main() { const totalLines = filesInfo?.total_lines ?? 0; // norms from individual tools + // (initialize defaults; individual normalizers can override) const norms: Norms = { // Correctness unitTestPassRate: -1, compilation: -1, - autofixSuccessRate: -1, + issuesFound: -1, + issuesFixed: -1, // Efficiency - tokenusage: -1, - timeToFirstWorkingSolution: -1, fixAttempts: -1, // Quality security: -1, @@ -192,58 +190,12 @@ function main() { } } - const categoryWeights: Record = { - correctness: { - unitTestPassRate: 40, - compilation: 30, - autofixSuccessRate: 30 - }, - quality: { - security: 25, - reliability: 20, - maintainability: 20, - duplication: 20, - performance: 15 - }, - efficiency: { - tokenusage: 35, - timeToFirstWorkingSolution: 35, - fixAttempts: 30 - } - }; - - const categoryScores: Record = {}; - for (const [category, weights] of Object.entries(categoryWeights)) { - categoryScores[category] = computeComposite(norms, weights); - } - - const categoryCount = Object.keys(categoryWeights).length || 1; - const score = Math.round( - (Object.values(categoryScores).reduce((sum, val) => sum + val, 0) / categoryCount) * 100 - ) / 100; - - // Write outputs - fs.writeFileSync("composite_score.txt", String(score), "utf8"); - - // Detailed per-category breakdown for workflow artifact - const breakdown = { - metrics: norms, - categories: categoryScores - }; - fs.writeFileSync("score_breakdown.json", JSON.stringify(breakdown, null, 2), "utf8"); - - // Full report for debugging/history - const fullReport = { - score, - norms, - categoryWeights, - categoryScores, - timestamp: new Date().toISOString() - }; - fs.writeFileSync("score_report.json", JSON.stringify(fullReport, null, 2), "utf8"); + // Primary output is just norms; logs are kept for visibility. + fs.writeFileSync("norms.json", JSON.stringify(norms, null, 2), "utf8"); + console.log("Norms:", norms); - console.log("Composite Score:", score); - console.log("Per-category breakdown:", breakdown); + return norms; } -main(); +const norms = main(); +export default norms; From dd9b263f4c5eadb02fc65163f0c40543d4e2fb09 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 12:43:46 +0500 Subject: [PATCH 11/24] score calculation changed --- tools/score.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/score.ts b/tools/score.ts index 000d798..80288b7 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -186,7 +186,10 @@ function main(): Norms { const sonarNorms = normFromSonarMeasure(sonarMap); for (const k of Object.keys(sonarNorms)) { const val = sonarNorms[k]; - if (typeof val === "number") norms[k] = Math.round(((norms[k] ?? 0) + val) / 2); + if (typeof val === "number") { + // Prefer Sonar-derived norm; overwrite any existing value. + norms[k] = val; + } } } From bdad2f1ebf70cf5c5f8505d2e9d9229fc9476f9d Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 12:48:30 +0500 Subject: [PATCH 12/24] reliabillity & secuirty scores normalized --- tools/score.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/score.ts b/tools/score.ts index 80288b7..03e7952 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -130,11 +130,19 @@ function normFromSonarMeasure(measures: Record) { norms.duplication = Math.max(0, Math.round(100 - dup)); // duplicated_lines_density: % - norms.reliability = Math.max(0, 100 - (measures.reliability_rating * 0.2)); - // reliability_rating: 1 (best) - 5 (worst) - - norms.security = Math.max(0, 100 - (measures.security_rating * 0.2)); - // security_rating: 1 (best) - 5 (worst) + const reliabilityRating = Number(measures.reliability_rating ?? 0); + norms.reliability = Math.max( + 0, + Math.min(100, ((5 - reliabilityRating) / 4) * 100) + ); + // reliability_rating: 1 (best) - 5 (worst), mapped so 1 -> 100, 5 -> 0 + + const securityRating = Number(measures.security_rating ?? 0); + norms.security = Math.max( + 0, + Math.min(100, ((5 - securityRating) / 4) * 100) + ); + // security_rating: 1 (best) - 5 (worst), mapped so 1 -> 100, 5 -> 0 return norms as Record; } From 9b89b72fda2e4068590ee16679c94388a14fc9ba Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 12:56:24 +0500 Subject: [PATCH 13/24] compilable score added --- .../ai-code-quality-sonarcloud-manual.yml | 28 ++++++++++++++++++- tools/AgentScoreCard.json | 1 + 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index 66b88d2..87eb9df 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -32,7 +32,33 @@ jobs: # removed semgrep from npm installs because Semgrep is installed/run via the official action (or via pipx/docker) - name: TypeScript compile (build project -> dist) - run: npx tsc -p tsconfig.json + continue-on-error: true + run: | + set +e + npx tsc -p tsconfig.json + EXIT_CODE=$? + + if [ "$EXIT_CODE" -eq 0 ]; then + COMPILED=1 + else + COMPILED=0 + fi + + node - < Date: Mon, 1 Dec 2025 13:01:19 +0500 Subject: [PATCH 14/24] Maintainability score adjusted --- tools/score.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/score.ts b/tools/score.ts index 03e7952..9e3c2b3 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -7,6 +7,7 @@ type AgentScoreCard = { fixAttempts?: number; issuesFound?: number; issuesFixed?: number; + compilable?: number; [key: string]: any; }; @@ -25,6 +26,7 @@ function applyAgentScoreCard(norms: Norms) { if (!scoreCard) return; const fieldMap: Record = { + compilation: "compilable", issuesFound: "issuesFound", issuesFixed: "issuesFixed", fixAttempts: "fixAttempts" @@ -118,7 +120,7 @@ function normFromSonarMeasure(measures: Record) { console.log("[Sonar] security_rating:", measures.security_rating); const smells = Number(measures.code_smells ?? 0); - norms.maintainability = Math.max(0, 100 - (smells * 0.2) - (measures.sqale_index * 0.2)); + norms.maintainability = Math.max(0, 100 - smells - (measures.sqale_index * 0.2)); // code_smells: # // sqale_index: mins From 3bddc3e361ad52b29dc700db50270a680cdd0526 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 14:44:44 +0500 Subject: [PATCH 15/24] clean up workflow --- .../ai-code-quality-sonarcloud-manual.yml | 24 +--- src/app.ts | 108 +++++++++--------- tools/score.ts | 56 --------- 3 files changed, 56 insertions(+), 132 deletions(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index 87eb9df..d1bf053 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -2,6 +2,9 @@ name: AI Code Quality - SonarCloud (Manual) on: workflow_dispatch: + push: + branches: + - '**' jobs: quality: @@ -27,9 +30,6 @@ jobs: - name: Install project & analysis dependencies run: | npm ci - npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin \ - typhonjs-escomplex typescript - # removed semgrep from npm installs because Semgrep is installed/run via the official action (or via pipx/docker) - name: TypeScript compile (build project -> dist) continue-on-error: true @@ -63,20 +63,7 @@ jobs: - name: Run tests (Jest) with coverage run: npx jest --coverage --coverageReporters=json-summary --coverageReporters=lcov || true - - name: Run ESLint (JSON) - run: npx eslint "src/**/*.{ts,tsx,js,jsx}" -f json -o eslint.json || true - - name: Run Semgrep (SAST) — official action - # Official Semgrep action will install and run Semgrep (no need to install with npm) - uses: returntocorp/semgrep-action@v1 - continue-on-error: true - with: - config: 'p/ci' - output: 'semgrep.json' - format: 'json' - - - name: Run escomplex (cyclomatic complexity) - run: npx typhonjs-escomplex -f json -o escomplex.json "src/**/*.ts" || true - name: Collect files/lines info run: | @@ -146,7 +133,6 @@ jobs: score_breakdown.json score_report.json sonar_metrics.json - semgrep.json - name: Print composite + detailed breakdown run: | @@ -165,7 +151,3 @@ jobs: echo "sonar metrics:" cat sonar_metrics.json fi - if [ -f semgrep.json ]; then - echo "semgrep metrics:" - cat semgrep.json - fi diff --git a/src/app.ts b/src/app.ts index 1eddcf8..971f473 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,38 +5,39 @@ import { CustomError, Environment, LogLevel, - OrdersController, -} from '@paypal/paypal-server-sdk'; + OrdersController +} from "@paypal/paypal-server-sdk"; -let cachedOrdersController: OrdersController | null = null; +type CreateOrderOptions = { + amount?: string; + currencyCode?: string; + intent?: CheckoutPaymentIntent; + referenceId?: string; + preferMinimalResponse?: boolean; +}; -function resolveEnvironment(rawValue: string | undefined): Environment { - const normalized = (rawValue ?? 'sandbox').trim().toLowerCase(); +let cachedOrdersController: OrdersController | null = null; - if (normalized === 'production' || normalized === 'live') { +function resolveEnvironment(rawValue: string | undefined | null): Environment { + const normalized = (rawValue ?? "sandbox").trim().toLowerCase(); + if (normalized === "production" || normalized === "live") { return Environment.Production; } - return Environment.Sandbox; } -function parseTimeout(rawTimeout: string | undefined): number { - const parsed = Number.parseInt(rawTimeout ?? '0', 10); +function parseTimeout(rawTimeout: string | undefined | null): number { + const parsed = Number.parseInt(rawTimeout ?? "0", 10); return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0; } -function resolveConfig(): { - clientId: string; - clientSecret: string; - environment: Environment; - timeout: number; -} { +function resolveConfig() { const clientId = process.env.PAYPAL_CLIENT_ID; const clientSecret = process.env.PAYPAL_CLIENT_SECRET; if (!clientId || !clientSecret) { throw new Error( - 'Missing PayPal credentials. Set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables before calling PayPal APIs.', + "Missing PayPal credentials. Set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables before calling PayPal APIs." ); } @@ -44,7 +45,7 @@ function resolveConfig(): { clientId, clientSecret, environment: resolveEnvironment(process.env.PAYPAL_ENV), - timeout: parseTimeout(process.env.PAYPAL_TIMEOUT_MS), + timeout: parseTimeout(process.env.PAYPAL_TIMEOUT_MS) }; } @@ -58,15 +59,15 @@ function getOrdersController(): OrdersController { const client = new Client({ clientCredentialsAuthCredentials: { oAuthClientId: clientId, - oAuthClientSecret: clientSecret, + oAuthClientSecret: clientSecret }, environment, timeout, logging: { logLevel: LogLevel.Info, logRequest: { logBody: true }, - logResponse: { logHeaders: true }, - }, + logResponse: { logHeaders: true } + } }); cachedOrdersController = new OrdersController(client); @@ -76,17 +77,18 @@ function getOrdersController(): OrdersController { function handlePayPalError(context: string, error: unknown): never { if (error instanceof ApiError) { console.error(`${context} (status: ${error.statusCode})`); - if (error.body) { - console.error('Response body:', JSON.stringify(error.body, null, 2)); + console.error("Response body:", JSON.stringify(error.body, null, 2)); } if (error instanceof CustomError && error.result) { - console.error('Error name:', error.result.name); - console.error('Error message:', error.result.message); - + console.error("Error name:", error.result.name); + console.error("Error message:", error.result.message); if (error.result.details) { - console.error('Error details:', JSON.stringify(error.result.details, null, 2)); + console.error( + "Error details:", + JSON.stringify(error.result.details, null, 2) + ); } } } else { @@ -96,23 +98,15 @@ function handlePayPalError(context: string, error: unknown): never { throw error instanceof Error ? error : new Error(String(error)); } -export interface CreateOrderOptions { - amount?: string; - currencyCode?: string; - intent?: CheckoutPaymentIntent; - referenceId?: string; - preferMinimalResponse?: boolean; -} - export async function createOrder( - options: CreateOrderOptions = {}, + options: CreateOrderOptions = {} ): Promise { const { - amount = '100.00', - currencyCode = 'USD', + amount = "100.00", + currencyCode = "USD", intent = CheckoutPaymentIntent.Capture, - referenceId = 'default', - preferMinimalResponse = false, + referenceId = "default", + preferMinimalResponse = false } = options; try { @@ -125,24 +119,23 @@ export async function createOrder( { referenceId, amount: { - currencyCode, - value: amount, - }, - }, - ], + ount + } + } + ] }, - prefer: preferMinimalResponse ? 'return=minimal' : 'return=representation', + prefer: preferMinimalResponse ? "return=minimal" : "return=representation" }); return createResponse.result; } catch (error) { - return handlePayPalError('Failed to create order', error); + return handlePayPalError("Failed to create order", error); } } export async function getOrder(orderId: string): Promise { if (!orderId) { - throw new Error('orderId is required to retrieve an order.'); + throw new Error("orderId is required to retrieve an order."); } try { @@ -150,26 +143,31 @@ export async function getOrder(orderId: string): Promise { const orderResponse = await controller.getOrder({ id: orderId }); return orderResponse.result; } catch (error) { - return handlePayPalError('Failed to retrieve order', error); + return handlePayPalError("Failed to retrieve order", error); } } async function runSample(): Promise { - if ((process.env.RUN_PAYPAL_ORDER_SAMPLE ?? '').toLowerCase() !== 'true') { + if ((process.env.RUN_PAYPAL_ORDER_SAMPLE ?? "").toLowerCase() !== "true") { return; } - const createdOrder: any = await createOrder(); + const createdOrder = await createOrder(); + const createdOrderId = + createdOrder && typeof createdOrder === "object" + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createdOrder as any).id + : undefined; - if (!createdOrder.id) { - console.warn('Created order is missing an ID. Skipping retrieval step.'); + if (!createdOrderId) { + console.warn("Created order is missing an ID. Skipping retrieval step."); return; } - const orderDetails: any = await getOrder(createdOrder.id); + const orderDetails = await getOrder(createdOrderId); - console.log('Created order status:', createdOrder.status); - console.log('Retrieved order status:', orderDetails.status); + console.log("Created order:", createdOrder); + console.log("Retrieved order:", orderDetails); } void runSample(); diff --git a/tools/score.ts b/tools/score.ts index 9e3c2b3..3785b5e 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -43,58 +43,6 @@ function applyAgentScoreCard(norms: Norms) { /* Normalizers */ -// coverage from coverage-summary.json (jest json-summary) -function normCoverage(cov: any): number { - try { - const pct = cov?.total?.lines?.pct ?? cov?.total?.lines?.percentage ?? 0; - return Math.round(Math.max(0, Math.min(100, pct))); - } catch { - return 0; - } -} - -// eslint.json => errors per KLOC -> norm -function normESLint(eslintJson: any, totalLines: number): number { - if (!eslintJson) return 100; - const reports = Array.isArray(eslintJson) ? eslintJson : []; - const totalMessages = reports.reduce((acc: number, r: any) => acc + (r.messages?.length ?? 0), 0); - const kloc = Math.max(0.001, totalLines / 1000); - const errorsPerKloc = totalMessages / kloc; - return Math.round(Math.max(0, Math.min(100, 100 - errorsPerKloc * 8))); -} - -// semgrep.json => severity-based penalty -function normSemgrep(semgrepJson: any): number { - if (!semgrepJson) return 100; - const results = semgrepJson.results ?? semgrepJson; - let score = 100; - for (const r of results) { - const sev = (r.extra?.severity || r.severity || "INFO").toString().toUpperCase(); - if (["CRITICAL", "HIGH"].includes(sev)) score -= 30; - else if (["MEDIUM"].includes(sev)) score -= 10; - else score -= 2; - } - return Math.max(0, score); -} - -// escomplex.json -> average cyclomatic -> norm -function normComplexity(escomplexJson: any): number { - if (!escomplexJson) return 100; - let vals: number[] = []; - const collect = (obj: any) => { - if (!obj || typeof obj !== "object") return; - for (const k of Object.keys(obj)) { - const v = obj[k]; - if (k.toLowerCase().includes("cyclomatic") && typeof v === "number") vals.push(v); - else if (Array.isArray(v)) v.forEach(collect); - else if (typeof v === "object") collect(v); - } - }; - collect(escomplexJson); - const avg = vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 1; - return Math.round(Math.max(0, Math.min(100, 100 - 5 * (avg - 1)))); -} - // Sonar metrics: sonar_metrics.json contains measures array function readSonarMetrics(sonarJson: any) { const out: Record = {}; @@ -161,10 +109,6 @@ function computeComposite(norms: Norms, weights: Weights): number { } function main(): Norms { - const coverage = safeRead("coverage/coverage-summary.json"); - const eslintJson = safeRead("eslint.json"); - const semgrepJson = safeRead("semgrep.json"); - const escomplexJson = safeRead("escomplex.json"); const filesInfo = safeRead("files_info.json") || { total_lines: 0 }; const sonarMetricsRaw = safeRead("sonar_metrics.json"); From 586605e623d2d5e74172e71f80e2ba9e1141379b Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 14:48:04 +0500 Subject: [PATCH 16/24] performance score changed --- src/app.ts | 3 ++- tools/score.ts | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app.ts b/src/app.ts index 971f473..4227447 100644 --- a/src/app.ts +++ b/src/app.ts @@ -119,7 +119,8 @@ export async function createOrder( { referenceId, amount: { - ount + currencyCode, + value: amount } } ] diff --git a/tools/score.ts b/tools/score.ts index 3785b5e..c3b588f 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -72,9 +72,8 @@ function normFromSonarMeasure(measures: Record) { // code_smells: # // sqale_index: mins - const complexity = Number(measures.complexity ?? 0); - norms.performance = Math.max(Math.max(0, 100 - complexity)); - // complexity: mins + norms.performance = Number(measures.complexity ?? 0); + // complexity: branches const dup = Number(measures.duplicated_lines_density ?? 0); norms.duplication = Math.max(0, Math.round(100 - dup)); From 3f8e49f3eced0297b3eb7b1a7a944093d1c1f870 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 15:43:26 +0500 Subject: [PATCH 17/24] test cases changes added --- .../ai-code-quality-sonarcloud-manual.yml | 57 +++++++++++--- package.json | 3 +- src/app.ts | 17 +++- tests/app.test.ts | 78 +++++++++++++++++++ tools/AgentScoreCard.json | 1 + tools/score.ts | 14 +++- tsconfig.json | 2 +- 7 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 tests/app.test.ts diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index d1bf053..2dac8b9 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -60,10 +60,50 @@ jobs: exit $EXIT_CODE - - name: Run tests (Jest) with coverage - run: npx jest --coverage --coverageReporters=json-summary --coverageReporters=lcov || true + - name: Run Node tests and update AgentScoreCard testsPassRate + run: | + # Run the Node test suite and capture output, but do not fail the workflow on test failure + node --test tests/app.test.ts > node-test-output.txt 2>&1 || true + + # Parse test counts from the Node test runner output and update tools/AgentScoreCard.json + node - <<'NODE' + const fs = require('fs'); + + const OUTPUT_PATH = 'node-test-output.txt'; + const SCORECARD_PATH = 'tools/AgentScoreCard.json'; + let totalTests = 0; + let passedTests = 0; + try { + const text = fs.readFileSync(OUTPUT_PATH, 'utf8'); + + // Lines look like: "ℹ tests 3" and "ℹ pass 3" + const testsMatch = text.match(/ℹ tests (\d+)/); + const passMatch = text.match(/ℹ pass (\d+)/); + + if (testsMatch) totalTests = Number(testsMatch[1]) || 0; + if (passMatch) passedTests = Number(passMatch[1]) || 0; + } catch (e) { + console.warn('Failed to read or parse node-test-output.txt', e); + } + + const passRate = totalTests > 0 ? passedTests / totalTests : 0; + + try { + const raw = fs.readFileSync(SCORECARD_PATH, 'utf8'); + const data = JSON.parse(raw); + data.testsPassRate = passRate; + fs.writeFileSync(SCORECARD_PATH, JSON.stringify(data, null, 2)); + console.log( + 'Updated AgentScoreCard.json testsPassRate to', + passRate, + `(${passedTests}/${totalTests})` + ); + } catch (e) { + console.warn('Failed to update AgentScoreCard.json testsPassRate', e); + } + NODE - name: Collect files/lines info run: | @@ -136,16 +176,9 @@ jobs: - name: Print composite + detailed breakdown run: | - if [ -f composite_score.txt ]; then - echo "Composite Score: $(cat composite_score.txt)" - fi - if [ -f score_breakdown.json ]; then - echo "Detailed Category Breakdown:" - cat score_breakdown.json - fi - if [ -f score_report.json ]; then - echo "Complete Score Card:" - cat score_report.json + if [ -f norms.json ]; then + echo "Score Card:" + cat norms.json fi if [ -f sonar_metrics.json ]; then echo "sonar metrics:" diff --git a/package.json b/package.json index 6c875e6..67af491 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "dependencies": { "@eslint/eslintrc": "^3.3.1", "@paypal/paypal-server-sdk": "^2.0.0", - "fs": "^0.0.1-security" + "fs": "^0.0.1-security", + "dotenv": "^16.4.5" }, "name": "ai_code_quality_sonarcloud", "version": "1.0.0", diff --git a/src/app.ts b/src/app.ts index 4227447..3d5b9e0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,3 +1,5 @@ +import "dotenv/config"; + import { ApiError, CheckoutPaymentIntent, @@ -8,6 +10,8 @@ import { OrdersController } from "@paypal/paypal-server-sdk"; +let cachedOrdersController: OrdersController | null = null; + type CreateOrderOptions = { amount?: string; currencyCode?: string; @@ -16,17 +20,17 @@ type CreateOrderOptions = { preferMinimalResponse?: boolean; }; -let cachedOrdersController: OrdersController | null = null; - function resolveEnvironment(rawValue: string | undefined | null): Environment { const normalized = (rawValue ?? "sandbox").trim().toLowerCase(); + if (normalized === "production" || normalized === "live") { return Environment.Production; } + return Environment.Sandbox; } -function parseTimeout(rawTimeout: string | undefined | null): number { +function parseTimeout(rawTimeout: string | undefined): number { const parsed = Number.parseInt(rawTimeout ?? "0", 10); return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0; } @@ -77,6 +81,7 @@ function getOrdersController(): OrdersController { function handlePayPalError(context: string, error: unknown): never { if (error instanceof ApiError) { console.error(`${context} (status: ${error.statusCode})`); + if (error.body) { console.error("Response body:", JSON.stringify(error.body, null, 2)); } @@ -84,6 +89,7 @@ function handlePayPalError(context: string, error: unknown): never { if (error instanceof CustomError && error.result) { console.error("Error name:", error.result.name); console.error("Error message:", error.result.message); + if (error.result.details) { console.error( "Error details:", @@ -125,7 +131,9 @@ export async function createOrder( } ] }, - prefer: preferMinimalResponse ? "return=minimal" : "return=representation" + prefer: preferMinimalResponse + ? "return=minimal" + : "return=representation" }); return createResponse.result; @@ -154,6 +162,7 @@ async function runSample(): Promise { } const createdOrder = await createOrder(); + const createdOrderId = createdOrder && typeof createdOrder === "object" ? // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tests/app.test.ts b/tests/app.test.ts new file mode 100644 index 0000000..1f83ab9 --- /dev/null +++ b/tests/app.test.ts @@ -0,0 +1,78 @@ +import "dotenv/config"; +import { strict as assert } from "node:assert"; +import test from "node:test"; + +import { createOrder, getOrder } from "../dist/src/app.js"; + +test("getOrder throws when orderId is empty", async () => { + await assert.rejects( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async () => getOrder("" as any), + (err: unknown) => { + assert.ok(err instanceof Error); + assert.equal( + (err as Error).message, + "orderId is required to retrieve an order." + ); + return true; + } + ); +}); + +test("createOrder fails with missing PayPal credentials", async () => { + const originalClientId = process.env.PAYPAL_CLIENT_ID; + const originalClientSecret = process.env.PAYPAL_CLIENT_SECRET; + + delete process.env.PAYPAL_CLIENT_ID; + delete process.env.PAYPAL_CLIENT_SECRET; + + try { + await assert.rejects( + async () => { + await createOrder(); + }, + (err: unknown) => { + assert.ok(err instanceof Error); + assert.equal( + (err as Error).message, + "Missing PayPal credentials. Set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables before calling PayPal APIs." + ); + return true; + } + ); + } finally { + if (originalClientId !== undefined) { + process.env.PAYPAL_CLIENT_ID = originalClientId; + } + if (originalClientSecret !== undefined) { + process.env.PAYPAL_CLIENT_SECRET = originalClientSecret; + } + } +}); + +test("createOrder then getOrder using returned id", async () => { + const createdOrder = await createOrder(); + + const createdOrderId = + createdOrder && typeof createdOrder === "object" + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createdOrder as any).id + : undefined; + + assert.ok(createdOrderId, "Expected created order to provide an id"); + + const fetchedOrder = await getOrder(createdOrderId as string); + + const fetchedOrderId = + fetchedOrder && typeof fetchedOrder === "object" + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + (fetchedOrder as any).id + : undefined; + + assert.equal( + fetchedOrderId, + createdOrderId, + "Fetched order should have the same id as the created order" + ); +}); + diff --git a/tools/AgentScoreCard.json b/tools/AgentScoreCard.json index 1d395a1..d5df46b 100644 --- a/tools/AgentScoreCard.json +++ b/tools/AgentScoreCard.json @@ -3,6 +3,7 @@ "issuesFixed": 1, "fixAttempts": 1, "compilable": 0, + "testsPassRate": 0, "examples": { "fixAttempts": 3, "issuesFound": 2, diff --git a/tools/score.ts b/tools/score.ts index c3b588f..95ee204 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -1,5 +1,5 @@ // tools/score.ts -import fs from "fs"; +import * as fs from "fs"; type Norms = Record; type Weights = Record; @@ -8,6 +8,7 @@ type AgentScoreCard = { issuesFound?: number; issuesFixed?: number; compilable?: number; + testsPassRate?: number; [key: string]: any; }; @@ -26,6 +27,7 @@ function applyAgentScoreCard(norms: Norms) { if (!scoreCard) return; const fieldMap: Record = { + unitTestPassRate: "testsPassRate", compilation: "compilable", issuesFound: "issuesFound", issuesFixed: "issuesFixed", @@ -121,8 +123,17 @@ function main(): Norms { compilation: -1, issuesFound: -1, issuesFixed: -1, + autoFixRate:0, // Efficiency + inputCacheWrite:0, + input:0, + cacheRead:0, + outputTokens:0, + tokenusage:0, + timeToFirstWorkingSolution:0, fixAttempts: -1, + //Cost + cost: 0, // Quality security: -1, reliability: -1, @@ -130,6 +141,7 @@ function main(): Norms { duplication: -1, performance: -1 }; + applyAgentScoreCard(norms); diff --git a/tsconfig.json b/tsconfig.json index e395720..a891525 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "module": "nodenext", "target": "esnext", - "types": [], + "types": ["node"], "sourceMap": true, "declaration": true, From 88227e35604397f25e7575c5d59b25e8f892aae2 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 15:46:38 +0500 Subject: [PATCH 18/24] packages updated --- package-lock.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/package-lock.json b/package-lock.json index 504ec6b..97c671b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@eslint/eslintrc": "^3.3.1", "@paypal/paypal-server-sdk": "^2.0.0", + "dotenv": "^16.4.5", "fs": "^0.0.1-security" }, "devDependencies": { @@ -528,6 +529,7 @@ "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", @@ -755,6 +757,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1025,6 +1028,18 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT" }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1144,6 +1159,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -1884,6 +1900,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2054,6 +2071,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From bbbed00bebbe944b2d4dafa3401d1fdcc7dfab48 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 15:54:16 +0500 Subject: [PATCH 19/24] failing test cases fixed --- tests/app.test.ts | 51 ++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/tests/app.test.ts b/tests/app.test.ts index 1f83ab9..774ef88 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -4,6 +4,10 @@ import test from "node:test"; import { createOrder, getOrder } from "../dist/src/app.js"; +const hasPayPalCredentials = + Boolean(process.env.PAYPAL_CLIENT_ID) && + Boolean(process.env.PAYPAL_CLIENT_SECRET); + test("getOrder throws when orderId is empty", async () => { await assert.rejects( // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -50,29 +54,36 @@ test("createOrder fails with missing PayPal credentials", async () => { } }); -test("createOrder then getOrder using returned id", async () => { - const createdOrder = await createOrder(); +if (!hasPayPalCredentials) { + test.skip( + "createOrder then getOrder using returned id (skipped: missing PAYPAL credentials)", + () => {} + ); +} else { + test("createOrder then getOrder using returned id", async () => { + const createdOrder = await createOrder(); - const createdOrderId = - createdOrder && typeof createdOrder === "object" - ? // eslint-disable-next-line @typescript-eslint/no-explicit-any - (createdOrder as any).id - : undefined; + const createdOrderId = + createdOrder && typeof createdOrder === "object" + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createdOrder as any).id + : undefined; - assert.ok(createdOrderId, "Expected created order to provide an id"); + assert.ok(createdOrderId, "Expected created order to provide an id"); - const fetchedOrder = await getOrder(createdOrderId as string); + const fetchedOrder = await getOrder(createdOrderId as string); - const fetchedOrderId = - fetchedOrder && typeof fetchedOrder === "object" - ? // eslint-disable-next-line @typescript-eslint/no-explicit-any - (fetchedOrder as any).id - : undefined; + const fetchedOrderId = + fetchedOrder && typeof fetchedOrder === "object" + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + (fetchedOrder as any).id + : undefined; - assert.equal( - fetchedOrderId, - createdOrderId, - "Fetched order should have the same id as the created order" - ); -}); + assert.equal( + fetchedOrderId, + createdOrderId, + "Fetched order should have the same id as the created order" + ); + }); +} From c1453d231829e46ab1e2c82510bdd1deb8a65352 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 16:12:28 +0500 Subject: [PATCH 20/24] test case fixes --- .github/workflows/ai-code-quality-sonarcloud-manual.yml | 9 +++++---- tests/app.test.ts | 5 ++++- tsconfig.json | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index 2dac8b9..b8cd3e4 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -63,7 +63,7 @@ jobs: - name: Run Node tests and update AgentScoreCard testsPassRate run: | # Run the Node test suite and capture output, but do not fail the workflow on test failure - node --test tests/app.test.ts > node-test-output.txt 2>&1 || true + node --test dist/tests/app.test.js > node-test-output.txt 2>&1 || true # Parse test counts from the Node test runner output and update tools/AgentScoreCard.json node - <<'NODE' @@ -78,9 +78,10 @@ jobs: try { const text = fs.readFileSync(OUTPUT_PATH, 'utf8'); - // Lines look like: "ℹ tests 3" and "ℹ pass 3" - const testsMatch = text.match(/ℹ tests (\d+)/); - const passMatch = text.match(/ℹ pass (\d+)/); + // Summary lines typically contain "tests N" and "pass M" + // e.g. "ℹ tests 3" / "ℹ pass 3" or similar. + const testsMatch = text.match(/tests\s+(\d+)/); + const passMatch = text.match(/pass\s+(\d+)/); if (testsMatch) totalTests = Number(testsMatch[1]) || 0; if (passMatch) passedTests = Number(passMatch[1]) || 0; diff --git a/tests/app.test.ts b/tests/app.test.ts index 774ef88..463dd9a 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -2,7 +2,10 @@ import "dotenv/config"; import { strict as assert } from "node:assert"; import test from "node:test"; -import { createOrder, getOrder } from "../dist/src/app.js"; +// IMPORTANT: +// This file is compiled by TypeScript into dist/tests/app.test.js. +// The relative import below is resolved at runtime from dist/tests to dist/src. +import { createOrder, getOrder } from "../src/app.js"; const hasPayPalCredentials = Boolean(process.env.PAYPAL_CLIENT_ID) && diff --git a/tsconfig.json b/tsconfig.json index a891525..bb29bc9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,5 +22,5 @@ "moduleDetection": "force", "skipLibCheck": true }, - "include": ["src/**/*", "tools/**/*"] + "include": ["src/**/*", "tools/**/*", "tests/**/*"] } From 98e59f1f56ca3cea0152246db3770d629c23244b Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 16:39:55 +0500 Subject: [PATCH 21/24] refactoring --- .../ai-code-quality-sonarcloud-manual.yml | 4 ++ tools/score.ts | 38 ++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index b8cd3e4..e9c0bd1 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -185,3 +185,7 @@ jobs: echo "sonar metrics:" cat sonar_metrics.json fi + if [ -f result.json ]; then + echo "Results:" + cat result.json + fi \ No newline at end of file diff --git a/tools/score.ts b/tools/score.ts index 95ee204..a62eed8 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -123,23 +123,15 @@ function main(): Norms { compilation: -1, issuesFound: -1, issuesFixed: -1, - autoFixRate:0, - // Efficiency - inputCacheWrite:0, - input:0, - cacheRead:0, - outputTokens:0, - tokenusage:0, - timeToFirstWorkingSolution:0, - fixAttempts: -1, - //Cost - cost: 0, + autoFixRate:0, // Quality security: -1, reliability: -1, maintainability: -1, duplication: -1, - performance: -1 + performance: -1, + // Efficiency + fixAttempts: -1 }; @@ -160,7 +152,27 @@ function main(): Norms { // Primary output is just norms; logs are kept for visibility. fs.writeFileSync("norms.json", JSON.stringify(norms, null, 2), "utf8"); - console.log("Norms:", norms); + + // Write a compact, comma-separated summary line to result.json + // Order: compilation, issuesFound, issuesFixed, autoFixRate, security, + // reliability, maintainability, duplication, performance, fixAttempts + const resultValues = [ + norms.compilation, + norms.issuesFound, + norms.issuesFixed, + norms.autoFixRate, + norms.security, + norms.reliability, + norms.maintainability, + norms.duplication, + norms.performance, + norms.fixAttempts + ]; + + const csvLine = resultValues.join(","); + fs.writeFileSync("result.json", csvLine, "utf8"); + + console.log("Norms summary written to result.json (CSV):", csvLine); return norms; } From 0caf0d8c303e0970136c899ded25b2db958a66e2 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 16:44:02 +0500 Subject: [PATCH 22/24] missing property added --- .github/workflows/ai-code-quality-sonarcloud-manual.yml | 3 +++ tools/score.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ai-code-quality-sonarcloud-manual.yml b/.github/workflows/ai-code-quality-sonarcloud-manual.yml index e9c0bd1..78ef841 100644 --- a/.github/workflows/ai-code-quality-sonarcloud-manual.yml +++ b/.github/workflows/ai-code-quality-sonarcloud-manual.yml @@ -178,14 +178,17 @@ jobs: - name: Print composite + detailed breakdown run: | if [ -f norms.json ]; then + echo "" echo "Score Card:" cat norms.json fi if [ -f sonar_metrics.json ]; then + echo "" echo "sonar metrics:" cat sonar_metrics.json fi if [ -f result.json ]; then + echo "" echo "Results:" cat result.json fi \ No newline at end of file diff --git a/tools/score.ts b/tools/score.ts index a62eed8..da7949f 100644 --- a/tools/score.ts +++ b/tools/score.ts @@ -157,6 +157,7 @@ function main(): Norms { // Order: compilation, issuesFound, issuesFixed, autoFixRate, security, // reliability, maintainability, duplication, performance, fixAttempts const resultValues = [ + norms.unitTestPassRate, norms.compilation, norms.issuesFound, norms.issuesFixed, From 218779cda950afd2a66746b4f8cf4b6c63b56dd1 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Mon, 1 Dec 2025 16:57:53 +0500 Subject: [PATCH 23/24] Removed app.ts code --- src/app.ts | 185 ----------------------------------------------------- 1 file changed, 185 deletions(-) diff --git a/src/app.ts b/src/app.ts index 3d5b9e0..e69de29 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,185 +0,0 @@ -import "dotenv/config"; - -import { - ApiError, - CheckoutPaymentIntent, - Client, - CustomError, - Environment, - LogLevel, - OrdersController -} from "@paypal/paypal-server-sdk"; - -let cachedOrdersController: OrdersController | null = null; - -type CreateOrderOptions = { - amount?: string; - currencyCode?: string; - intent?: CheckoutPaymentIntent; - referenceId?: string; - preferMinimalResponse?: boolean; -}; - -function resolveEnvironment(rawValue: string | undefined | null): Environment { - const normalized = (rawValue ?? "sandbox").trim().toLowerCase(); - - if (normalized === "production" || normalized === "live") { - return Environment.Production; - } - - return Environment.Sandbox; -} - -function parseTimeout(rawTimeout: string | undefined): number { - const parsed = Number.parseInt(rawTimeout ?? "0", 10); - return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0; -} - -function resolveConfig() { - const clientId = process.env.PAYPAL_CLIENT_ID; - const clientSecret = process.env.PAYPAL_CLIENT_SECRET; - - if (!clientId || !clientSecret) { - throw new Error( - "Missing PayPal credentials. Set PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET environment variables before calling PayPal APIs." - ); - } - - return { - clientId, - clientSecret, - environment: resolveEnvironment(process.env.PAYPAL_ENV), - timeout: parseTimeout(process.env.PAYPAL_TIMEOUT_MS) - }; -} - -function getOrdersController(): OrdersController { - if (cachedOrdersController) { - return cachedOrdersController; - } - - const { clientId, clientSecret, environment, timeout } = resolveConfig(); - - const client = new Client({ - clientCredentialsAuthCredentials: { - oAuthClientId: clientId, - oAuthClientSecret: clientSecret - }, - environment, - timeout, - logging: { - logLevel: LogLevel.Info, - logRequest: { logBody: true }, - logResponse: { logHeaders: true } - } - }); - - cachedOrdersController = new OrdersController(client); - return cachedOrdersController; -} - -function handlePayPalError(context: string, error: unknown): never { - if (error instanceof ApiError) { - console.error(`${context} (status: ${error.statusCode})`); - - if (error.body) { - console.error("Response body:", JSON.stringify(error.body, null, 2)); - } - - if (error instanceof CustomError && error.result) { - console.error("Error name:", error.result.name); - console.error("Error message:", error.result.message); - - if (error.result.details) { - console.error( - "Error details:", - JSON.stringify(error.result.details, null, 2) - ); - } - } - } else { - console.error(`${context}:`, error); - } - - throw error instanceof Error ? error : new Error(String(error)); -} - -export async function createOrder( - options: CreateOrderOptions = {} -): Promise { - const { - amount = "100.00", - currencyCode = "USD", - intent = CheckoutPaymentIntent.Capture, - referenceId = "default", - preferMinimalResponse = false - } = options; - - try { - const controller = getOrdersController(); - - const createResponse = await controller.createOrder({ - body: { - intent, - purchaseUnits: [ - { - referenceId, - amount: { - currencyCode, - value: amount - } - } - ] - }, - prefer: preferMinimalResponse - ? "return=minimal" - : "return=representation" - }); - - return createResponse.result; - } catch (error) { - return handlePayPalError("Failed to create order", error); - } -} - -export async function getOrder(orderId: string): Promise { - if (!orderId) { - throw new Error("orderId is required to retrieve an order."); - } - - try { - const controller = getOrdersController(); - const orderResponse = await controller.getOrder({ id: orderId }); - return orderResponse.result; - } catch (error) { - return handlePayPalError("Failed to retrieve order", error); - } -} - -async function runSample(): Promise { - if ((process.env.RUN_PAYPAL_ORDER_SAMPLE ?? "").toLowerCase() !== "true") { - return; - } - - const createdOrder = await createOrder(); - - const createdOrderId = - createdOrder && typeof createdOrder === "object" - ? // eslint-disable-next-line @typescript-eslint/no-explicit-any - (createdOrder as any).id - : undefined; - - if (!createdOrderId) { - console.warn("Created order is missing an ID. Skipping retrieval step."); - return; - } - - const orderDetails = await getOrder(createdOrderId); - - console.log("Created order:", createdOrder); - console.log("Retrieved order:", orderDetails); -} - -void runSample(); - - From 64277d09bf131dcc6ba794b86a7b7d8d68f59147 Mon Sep 17 00:00:00 2001 From: mohammadali2549 Date: Tue, 2 Dec 2025 09:33:10 +0500 Subject: [PATCH 24/24] code cleaning --- eslint.config.js | 25 ------------------------- package-lock.json | 17 ----------------- package.json | 1 - 3 files changed, 43 deletions(-) delete mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 19cca33..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,25 +0,0 @@ -// eslint.config.js -import typescript from "@typescript-eslint/eslint-plugin"; -import tsParser from "@typescript-eslint/parser"; - -export default [ - { - files: ["**/*.ts"], - ignores: ["dist/**", "node_modules/**"], - - languageOptions: { - parser: tsParser, - parserOptions: { - project: "./tsconfig.json", - }, - }, - - plugins: { - "@typescript-eslint": typescript, - }, - - rules: { - ...typescript.configs["recommended"].rules, - }, - }, -]; diff --git a/package-lock.json b/package-lock.json index 97c671b..c33bb86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "ISC", "dependencies": { "@eslint/eslintrc": "^3.3.1", - "@paypal/paypal-server-sdk": "^2.0.0", "dotenv": "^16.4.5", "fs": "^0.0.1-security" }, @@ -443,22 +442,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@paypal/paypal-server-sdk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@paypal/paypal-server-sdk/-/paypal-server-sdk-2.0.0.tgz", - "integrity": "sha512-aMahhIWIRspzr7Rqwh2QNcfAU1vvhXiuJgli6XCAtHue92ehGadRaUQefnaCuoalD532CV4ouR659x5xefcCiA==", - "license": "MIT", - "dependencies": { - "@apimatic/authentication-adapters": "^0.5.14", - "@apimatic/axios-client-adapter": "^0.3.20", - "@apimatic/core": "^0.10.28", - "@apimatic/oauth-adapters": "^0.4.18", - "@apimatic/schema": "^0.7.21" - }, - "engines": { - "node": ">=14.17.0" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", diff --git a/package.json b/package.json index 67af491..4238908 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "dependencies": { "@eslint/eslintrc": "^3.3.1", - "@paypal/paypal-server-sdk": "^2.0.0", "fs": "^0.0.1-security", "dotenv": "^16.4.5" },