feat(rust): split core library into activate/license/heartbeat modules with build.rs and C ABI tests

This commit is contained in:
2026-04-28 18:46:20 +08:00
parent 6b3f1bdab5
commit 6a92f46447
14 changed files with 769 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
use std::ffi::CString;
use std::os::raw::c_char;
#[link(name = "craftlabs_auth_bitanswer")]
extern "C" {
fn craft_initialize(config_json: *const c_char) -> *mut std::ffi::c_void;
fn craft_activate(
handle: *mut std::ffi::c_void,
license_key: *const c_char,
config_json: *const c_char,
) -> CraftResult;
fn craft_check_license(handle: *mut std::ffi::c_void) -> CraftResult;
fn craft_get_license_info(handle: *mut std::ffi::c_void) -> *mut LicenseInfo;
fn craft_free_license_info(info: *mut LicenseInfo);
fn craft_has_feature(handle: *mut std::ffi::c_void, feature_name: *const c_char) -> i32;
fn craft_release(handle: *mut std::ffi::c_void) -> CraftResult;
fn craft_heartbeat(handle: *mut std::ffi::c_void) -> CraftResult;
fn craft_destroy(handle: *mut std::ffi::c_void);
}
#[repr(C)]
struct CraftResult {
success: i32,
message: *const c_char,
}
#[repr(C)]
struct LicenseInfo {
is_licensed: i32,
expiration_date: i64,
feature_names: *const *const c_char,
feature_values: *const i32,
feature_count: i32,
}
#[test]
fn test_initialize_and_destroy() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
unsafe { craft_destroy(handle) };
}
#[test]
fn test_initialize_with_null_config() {
let handle = unsafe { craft_initialize(std::ptr::null()) };
assert!(!handle.is_null());
unsafe { craft_destroy(handle) };
}
#[test]
fn test_activate() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let key = CString::new("test-license-key").unwrap();
let cfg = CString::new("{}").unwrap();
let result = unsafe { craft_activate(handle, key.as_ptr(), cfg.as_ptr()) };
assert_eq!(result.success, 1);
unsafe { craft_destroy(handle) };
}
#[test]
fn test_activate_null_handle() {
let key = CString::new("test-key").unwrap();
let cfg = CString::new("{}").unwrap();
let result = unsafe { craft_activate(std::ptr::null_mut(), key.as_ptr(), cfg.as_ptr()) };
assert_eq!(result.success, 0);
}
#[test]
fn test_check_license() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let result = unsafe { craft_check_license(handle) };
assert_eq!(result.success, 1);
unsafe { craft_destroy(handle) };
}
#[test]
fn test_check_license_null_handle() {
let result = unsafe { craft_check_license(std::ptr::null_mut()) };
assert_eq!(result.success, 0);
}
#[test]
fn test_get_license_info() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let info = unsafe { craft_get_license_info(handle) };
assert!(!info.is_null());
unsafe {
assert_eq!((*info).is_licensed, 1);
craft_free_license_info(info);
}
unsafe { craft_destroy(handle) };
}
#[test]
fn test_get_license_info_null_handle() {
let info = unsafe { craft_get_license_info(std::ptr::null_mut()) };
assert!(info.is_null());
}
#[test]
fn test_has_feature() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let feature = CString::new("premium").unwrap();
let result = unsafe { craft_has_feature(handle, feature.as_ptr()) };
assert_eq!(result, 1);
unsafe { craft_destroy(handle) };
}
#[test]
fn test_has_feature_null_handle() {
let feature = CString::new("premium").unwrap();
let result = unsafe { craft_has_feature(std::ptr::null_mut(), feature.as_ptr()) };
assert_eq!(result, 0);
}
#[test]
fn test_release() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let result = unsafe { craft_release(handle) };
assert_eq!(result.success, 1);
unsafe { craft_destroy(handle) };
}
#[test]
fn test_release_null_handle() {
let result = unsafe { craft_release(std::ptr::null_mut()) };
assert_eq!(result.success, 0);
}
#[test]
fn test_heartbeat() {
let config = CString::new("{}").unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let result = unsafe { craft_heartbeat(handle) };
assert_eq!(result.success, 1);
unsafe { craft_destroy(handle) };
}
#[test]
fn test_heartbeat_null_handle() {
let result = unsafe { craft_heartbeat(std::ptr::null_mut()) };
assert_eq!(result.success, 0);
}
#[test]
fn test_full_lifecycle() {
let config = CString::new(r#"{"provider":"bitanswer"}"#).unwrap();
let handle = unsafe { craft_initialize(config.as_ptr()) };
assert!(!handle.is_null());
let key = CString::new("TEST-LICENSE-KEY").unwrap();
let cfg = CString::new("{}").unwrap();
let result = unsafe { craft_activate(handle, key.as_ptr(), cfg.as_ptr()) };
assert_eq!(result.success, 1);
let result = unsafe { craft_check_license(handle) };
assert_eq!(result.success, 1);
let info = unsafe { craft_get_license_info(handle) };
assert!(!info.is_null());
unsafe {
assert_eq!((*info).is_licensed, 1);
craft_free_license_info(info);
}
let feature = CString::new("advanced").unwrap();
let has_feature = unsafe { craft_has_feature(handle, feature.as_ptr()) };
assert_eq!(has_feature, 1);
let result = unsafe { craft_heartbeat(handle) };
assert_eq!(result.success, 1);
let result = unsafe { craft_release(handle) };
assert_eq!(result.success, 1);
unsafe { craft_destroy(handle) };
}