ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
155 lines
10 KiB
JavaScript
155 lines
10 KiB
JavaScript
// 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;
|
|
}
|
|
`);
|