Files
story/firmware/echoear-platform-agent/tests/call-client-host.cjs
T

155 lines
10 KiB
JavaScript
Raw Normal View History

// Executes the actual device adapter against deterministic transport/NVS/clock
// doubles. These tests are NOT supplier release evidence or hardware acceptance.
const fs=require('node:fs'),path=require('node:path');
const base=path.join(__dirname,'..');
const jsonDir=path.join(process.env.IDF_PATH||'E:/Codex/toolchains/esp-idf-v5.5.1','components/json/cJSON');
const strip=s=>s.replace(/^#include[^\n]*$/gm,'').replace(/^#pragma once/gm,'');
const sdk=strip(fs.readFileSync(path.join(base,'components/volc_conv_ai/src/base/volc_device_manager.h'),'utf8'));
const version=strip(fs.readFileSync(path.join(base,'main/firmware_version.h'),'utf8'));
process.stdout.write(fs.readFileSync(path.join(jsonDir,'cJSON.h'),'utf8')+'\n'+fs.readFileSync(path.join(jsonDir,'cJSON.c'),'utf8').replace('#include "cJSON.h"','')+`
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdatomic.h>
#include <sys/time.h>
typedef int esp_err_t;
typedef int nvs_handle_t;
typedef int StaticSemaphore_t;
typedef int SemaphoreHandle_t;
#define ESP_OK 0
#define ESP_FAIL -1
#define ESP_ERR_NO_MEM 2
#define ESP_ERR_INVALID_STATE 3
#define ESP_ERR_INVALID_RESPONSE 4
#define ESP_ERR_INVALID_SIZE 5
#define ESP_ERR_TIMEOUT 6
#define ESP_ERR_INVALID_ARG 7
#define ESP_ERR_NVS_NOT_FOUND 7
#define NVS_READWRITE 1
#define NVS_READONLY 0
#define pdTRUE 1
#define pdPASS 1
#define portMAX_DELAY 0
#define pdMS_TO_TICKS(x) (x)
#define ESP_LOGI(...) ((void)0)
static int64_t test_us=1000000;
static int64_t esp_timer_get_time(void){return test_us;}
static int xSemaphoreCreateMutexStatic(void *p){return 1;}
static int xSemaphoreTake(int m,int t){return pdTRUE;}
static void xSemaphoreGive(int m){}
static void vTaskDelay(int t){}
static int xTaskCreate(void(*f)(void*),const char*n,int stack,void*a,int priority,void*out){return pdPASS;}
static int nvs_open(const char *s,int mode,int *n){*n=1;return 0;}
static int credential_writes, storage_error; static unsigned char saved_record[2048]; static size_t saved_len;
static int nvs_set_blob(int n,const char *key,const void *data,size_t len){if(!strcmp(key,"credential")){credential_writes++;if(!storage_error){assert(len<=sizeof(saved_record));memcpy(saved_record,data,len);saved_len=len;}}return 0;}
static int nvs_get_blob(int n,const char *key,void *data,size_t *len){if(!strcmp(key,"credential")&&saved_len==*len){memcpy(data,saved_record,saved_len);return 0;}return ESP_ERR_NVS_NOT_FOUND;}
static int nvs_erase_key(int n,const char *key){return 0;}
static int nvs_commit(int n){return storage_error;}
static void nvs_close(int n){}
static void mbedtls_platform_zeroize(void*p,size_t n){memset(p,0,n);}
static void mbedtls_sha256(const unsigned char*p,size_t n,unsigned char*out,int x){memset(out,0,32);for(size_t i=0;i<n;i++)out[i%32]^=p[i];}
static bool ble_cloud_configured(void){return true;}
static const char*ble_factory_id(void){return "KP01-000000000001";}
static const char*ble_factory_model(void){return "ECHOEAR-S3";}
static const char*ble_cloud_boot_id(void){return "11111111-1111-4111-8111-111111111111";}
static const char*ble_cloud_challenge_id(void){return ble_cloud_boot_id();}
static int ble_cloud_sync_time(void){return 0;}
static char *test_config;
static int config_mode, imported, supplier_calls;
static char *ble_cloud_decode(const char*d,const char*e,const char*s){return test_config?strdup(test_config):NULL;}
static unsigned uuid_counter;
static void ble_random_uuid(char out[37]){snprintf(out,37,"11111111-1111-4111-8111-%012u",++uuid_counter);}
`+sdk+'\n'+version+`
int volc_device_register(volc_iot_info_t*i,char**o){supplier_calls++;return -1;}
`+strip(fs.readFileSync(path.join(base,'main/platform_client.h'),'utf8'))+'\n'+strip(fs.readFileSync(path.join(base,'main/call_client.h'),'utf8'))+`
static int response_mode, requests;
static char *last_body;
static char last_id[37];
static int64_t request_advance;
const char *platform_last_error_code(void) {return response_mode==2 ? "CHAT_ENDED" : "";}
esp_err_t platform_request(const char*method,const char*path,const char*body,const char*rid,char**out){
requests++; free(last_body);last_body=strdup(body);snprintf(last_id,37,"%s",rid?rid:"");
if(response_mode) return ESP_FAIL;
cJSON *j=cJSON_Parse(body),*root=cJSON_CreateObject(),*data=cJSON_AddObjectToObject(root,"data");cJSON_AddBoolToObject(root,"ok",true);
assert(!strstr(path,"/voice-runtime")&&!strstr(path,"/activation-")&&!strstr(path,"/device/register"));
if(strstr(path,"/ble-state"))cJSON_AddBoolToObject(data,"bound",config_mode!=4);
if(strstr(path,"/voice-config")){
imported++;
cJSON_AddStringToObject(data,"voice_status",config_mode==1?"preparing":"ready");
cJSON *iot=cJSON_AddObjectToObject(data,"voice");
cJSON_AddStringToObject(iot,"instance_id","instance");cJSON_AddStringToObject(iot,"product_key","product");
cJSON_AddStringToObject(iot,"device_name",ble_factory_id());cJSON_AddStringToObject(iot,"device_secret","fixture-device-secret-123456789");
if(config_mode!=3)cJSON_AddStringToObject(iot,"RTCAppID","rtc-app");
}
if(strstr(path,"/chat/start")){
struct timeval tv;gettimeofday(&tv,NULL);int64_t now=(int64_t)tv.tv_sec*1000+tv.tv_usec/1000+2000;
cJSON_AddNumberToObject(root,"server_time_ms",now);
cJSON_AddStringToObject(data,"session_id",cJSON_GetStringValue(cJSON_GetObjectItem(j,"session_id")));
cJSON_AddStringToObject(data,"bot_id","bot");cJSON_AddObjectToObject(data,"params");
cJSON_AddNumberToObject(data,"lease_expires_at_ms",now+45000);cJSON_AddNumberToObject(data,"expires_at_ms",now+3600000);
test_us+=request_advance*1000;
}
if(out)*out=cJSON_PrintUnformatted(root);cJSON_Delete(j);cJSON_Delete(root);return 0;
}
`+strip(fs.readFileSync(path.join(base,'main/call_client.c'),'utf8'))+`
static void reset(void){
free(params);params=NULL;memset(&call,0,sizeof(call));call.version=2;
snprintf(call.session,37,"%s",ble_cloud_boot_id());snprintf(call.boot,37,"%s",ble_cloud_boot_id());
snprintf(call.end_id,37,"%s",ble_cloud_boot_id());test_us=1000000;
atomic_store(&audio_allowed,true);atomic_store(&lease_deadline,mono()+45000);
response_mode=requests=0;request_advance=0;state_mutex=1;engine_ready=true;sdk_stopped=false;
}
int main(void){
cJSON *bound=cJSON_CreateObject();
cJSON_AddStringToObject(bound,"instance_id","instance");cJSON_AddStringToObject(bound,"product_key","product");
cJSON_AddStringToObject(bound,"device_name","device");cJSON_AddStringToObject(bound,"device_secret","fixture-device-secret-123456789");
assert(call_client_save_bind_voice(bound)==ESP_ERR_INVALID_RESPONSE);assert(credential_writes==0);
cJSON_AddStringToObject(bound,"RTCAppID","rtc-app");
assert(call_client_save_bind_voice(bound)==ESP_OK);assert(credential_writes==1);
storage_error=ESP_FAIL;assert(call_client_save_bind_voice(bound)==ESP_FAIL);storage_error=0;cJSON_Delete(bound);
reset();assert(call_client_audio_allowed());test_us+=45000000;assert(!call_client_audio_allowed());
reset();assert(volc_platform_recording_prepare("app","room","device")==ESP_OK);
assert(strstr(last_body,"rtc_app_id")&&strstr(last_body,"room_id")&&strstr(last_body,"rtc_user_id"));
assert(!strstr(last_body,"device_secret")&&!strstr(last_body,"audio"));
int recorded_requests=requests;call.finished=true;
assert(volc_platform_recording_prepare("app","room","device")==ESP_ERR_INVALID_STATE);assert(requests==recorded_requests);
reset();response_mode=1;assert(volc_platform_recording_prepare("app","room","device")!=ESP_OK);assert(call_client_audio_allowed());
reset();assert(fetch_parameters(true)==0);assert(params && !strcmp(bot,"bot"));
reset();request_advance=46000;assert(fetch_parameters(false)!=0);assert(!call_client_audio_allowed());
reset();int before=requests;assert(call_client_turn("subject","1","private text")==0);assert(requests==before);
reset();call.finished=true;call.sdk_stopped=true;strcpy(call.reason,"hangup");response_mode=1;
finish_pending();assert(call.session[0]);char *original=strdup(last_body);char rid[37];strcpy(rid,last_id);
response_mode=0;finish_pending();assert(!call.session[0]);assert(!strcmp(original,last_body)&&!strcmp(rid,last_id));free(original);
reset();response_mode=2;renew_due=0;assert(call_client_poll()==1);assert(!call_client_audio_allowed());
reset();memset(&call,0,sizeof(call));response_mode=1;assert(call_client_begin()!=0);assert(call.finished&&call.session[0]);
response_mode=0;finish_pending();assert(!call.session[0]);
reset();memset(&call,0,sizeof(call));memset(&voice,0,sizeof(voice));boot_loaded=false;config_mode=0;imported=0;
assert(call_client_boot()==ESP_OK);assert(!strcmp(voice.rtc_app,"rtc-app"));assert(imported==0);assert(supplier_calls==0);
before=requests;call_client_engine_ready(true);assert(engine_ready);assert(requests==before);
config_mode=4;assert(call_client_boot()==ESP_ERR_INVALID_STATE);assert(!strcmp(call_client_start_error(),"DEVICE_UNBOUND"));assert(!call_client_audio_allowed());
config_mode=0;memset(&voice,0,sizeof(voice));saved_len=0;boot_loaded=false;
assert(call_client_boot()==ESP_OK);assert(imported==1);assert(supplier_calls==0);
memset(&voice,0,sizeof(voice));saved_len=0;boot_loaded=false;config_mode=1;
assert(call_client_boot()==ESP_ERR_INVALID_STATE);assert(!strcmp(call_client_start_error(),"VOICE_PREPARING"));
config_mode=3;assert(call_client_boot()==ESP_ERR_INVALID_RESPONSE);assert(supplier_calls==0);
config_mode=0;assert(call_client_boot()==ESP_OK);
// A disruptive operation atomically excludes a new call, including a race
// between command delivery and the user's next screen tap.
memset(&call,0,sizeof(call));call_client_engine_ready(true);
assert(!call_client_busy());assert(call_client_enter_maintenance());
assert(call_client_busy());assert(call_client_begin()==ESP_ERR_INVALID_STATE);
assert(call_client_set_standby(true)==ESP_OK);
call_client_leave_maintenance();assert(!call_client_busy());
assert(call_client_standby());assert(call_client_begin()==ESP_ERR_INVALID_STATE);
assert(call_client_set_standby(false)==ESP_ERR_INVALID_STATE);
assert(call_client_enter_maintenance());assert(call_client_set_standby(false)==ESP_OK);
call_client_leave_maintenance();assert(!call_client_standby());
strcpy(call.session,"11111111-1111-4111-8111-111111111111");
assert(call_client_busy());assert(!call_client_enter_maintenance());
free(test_config);test_config=NULL;
reset();free(params);free(last_body);puts("PASS direct chat C: bind credential persistence, cached boot, recovery only, no activation handshake, parameter fetch, stable end retry, unbound refusal and lease expiry");return 0;
}
`);