import { test, expect } from '@playwright/test' import topics from '../topic.json' import crypto from 'crypto' // Configuration const API_URL = 'https://api-dev.vegacloud.id' const API_COMMAND_PATH = '/device/v1/command' const API_STATUS_PATH = '/device/v1/status' const API_KEY = 'ak_sdbx:N8Xbvb4OYprJ-Mtw4QO6xw' const SECRET_KEY = 'sk_sdbx:zyJteO7W3szL41fWWB8WZEmKpp8KyJzi-9o9OovbpT4' // Convert "01" -> "A", "02" -> "B", etc. function numToAlpha(numStr: string): string { const num = parseInt(numStr, 10) if (isNaN(num)) return numStr return String.fromCharCode(64 + num) } // Helper function to sort keys deeply function sortKeysDeep(obj: any): any { if (Array.isArray(obj)) { return obj.map(sortKeysDeep) } if (obj !== null && typeof obj === 'object') { return Object.keys(obj) .sort() .reduce((acc: any, key) => { acc[key] = sortKeysDeep(obj[key]) return acc }, {}) } return obj } // Generate signature for POST request function generateSignatureForPost(body: any): { timestamp: number; nonce: string; signature: string } { const timestamp = Math.floor(Date.now() / 1000) const nonce = crypto.randomUUID() const sortedBody = sortKeysDeep(body) const jsonBody = JSON.stringify({ timestamp, nonce, body: JSON.stringify(sortedBody) }) const signature = crypto .createHmac('sha256', SECRET_KEY) .update(jsonBody) .digest('hex') return { timestamp, nonce, signature } } // Parse query params from URL manually (matching Postman logic) function parseQueryParamsManual(url: string): Record { const resultObject: Record = {} const queryStartIndex = url.indexOf('?') if (queryStartIndex === -1 || queryStartIndex === url.length - 1) { return resultObject } const queryString = url.substring(queryStartIndex + 1) const pairs = queryString.split('&') for (let i = 0; i < pairs.length; i++) { const pair = pairs[i] const parts = pair.split('=') if (parts.length === 2) { const key = decodeURIComponent(parts[0].replace(/\+/g, ' ')) const value = decodeURIComponent(parts[1].replace(/\+/g, ' ')) resultObject[key] = value } } return resultObject } // Generate signature for GET request function generateSignatureForGet(url: string): { timestamp: number; nonce: string; signature: string } { const timestamp = Math.floor(Date.now() / 1000) const nonce = crypto.randomUUID() const params = parseQueryParamsManual(url) const sortedParams = Object.keys(params) .sort() .reduce((acc: any, key) => { acc[key] = params[key] return acc }, {}) const jsonBody = JSON.stringify({ timestamp, nonce, body: JSON.stringify(sortedParams) }) const signature = crypto .createHmac('sha256', SECRET_KEY) .update(jsonBody) .digest('hex') return { timestamp, nonce, signature } } test.describe('Device Command & Status Tests', () => { const getPayload = (deviceName: string) => { if (deviceName === 'AC') return 'On' if (deviceName === 'BL') return 'Open' if (deviceName === 'DL') return 'Lock' return null } test.describe('1. Command API Tests', () => { for (const item of topics) { const { towerNumber, floorName, unitNumber: originalUnit, deviceName, deviceRoom, deviceType } = item const action = getPayload(deviceName) if (action) { const unitNumber = numToAlpha(originalUnit) const payload = { merchantName: 'SAVY', floorName, unitNumber, deviceName, roomName: deviceRoom, deviceType, commandType: 'C', towerNumber, payload: { action } } test(`POST Command: ${deviceName} (${deviceType}) - ${floorName}_${unitNumber} - ${deviceRoom}`, async ({ request }) => { const { timestamp, nonce, signature } = generateSignatureForPost(payload) console.log(`[Command] Request: ${JSON.stringify(payload, null, 2)}`) const response = await request.post(`${API_URL}${API_COMMAND_PATH}`, { headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-Timestamp': timestamp.toString(), 'X-Nonce': nonce, 'X-Signature': signature }, data: payload }) const responseBody = await response.json() console.log(`[Command] Response Status: ${response.status()}`) console.log(`[Command] Response Body: ${JSON.stringify(responseBody, null, 2)}`) console.log('---') expect(response.status()).toBe(200) }) } } }) test.describe('2. Status Check API Tests', () => { for (const item of topics) { const { towerNumber, floorName, unitNumber: originalUnit, deviceName, deviceRoom, deviceType } = item const action = getPayload(deviceName) if (action) { const unitNumber = numToAlpha(originalUnit) test(`GET Status: ${deviceName} (${deviceType}) - ${floorName}_${unitNumber} - ${deviceRoom}`, async ({ request }) => { const params = { merchantName: 'SAVY', floorName, unitNumber, deviceName, roomName: deviceRoom, deviceType, commandType: 'S', towerNumber } const queryString = new URLSearchParams(params).toString() const url = `${API_URL}${API_STATUS_PATH}?${queryString}` const { timestamp, nonce, signature } = generateSignatureForGet(url) console.log(`[Status Check] Request URL: ${url}`) const response = await request.get(url, { headers: { 'X-API-Key': API_KEY, 'X-Timestamp': timestamp.toString(), 'X-Nonce': nonce, 'X-Signature': signature } }) const responseBody = await response.json() console.log(`[Status Check] Response Status: ${response.status()}`) console.log(`[Status Check] Response Body: ${JSON.stringify(responseBody, null, 2)}`) console.log('---') expect(response.status()).toBe(200) expect(responseBody.data).not.toBeNull() // Strict Validation expect(responseBody.data.floorName).toBe(floorName) expect(responseBody.data.unitNumber).toBe(unitNumber) expect(responseBody.data.deviceName).toBe(deviceName) expect(responseBody.data.roomName).toBe(deviceRoom) expect(responseBody.data.deviceType).toBe(deviceType) expect(responseBody.data.code).toBe('S') expect(responseBody.data.towerNumber).toBe(towerNumber) expect(responseBody.data).toHaveProperty('payload') }) } } }) })