mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2026-03-03 02:57:01 +00:00
Adjustments for quickbooks serialization
This commit is contained in:
@@ -30,10 +30,9 @@ class QuickbooksSettingsCast implements CastsAttributes
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
if ($value instanceof QuickbooksSettings) {
|
||||
return json_encode(get_object_vars($value));
|
||||
return json_encode($value->toArray());
|
||||
}
|
||||
|
||||
return null;
|
||||
// return json_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,16 @@ class QuickbooksSettings implements Castable
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'accessTokenKey' => $this->accessTokenKey,
|
||||
'refresh_token' => $this->refresh_token,
|
||||
'realmID' => $this->realmID,
|
||||
'accessTokenExpiresAt' => $this->accessTokenExpiresAt,
|
||||
'refreshTokenExpiresAt' => $this->refreshTokenExpiresAt,
|
||||
'baseURL' => $this->baseURL,
|
||||
'settings' => $this->settings->toArray(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,21 @@ class QuickbooksSync
|
||||
$this->default_income_account = $attributes['default_income_account'] ?? '';
|
||||
$this->default_expense_account = $attributes['default_expense_account'] ?? '';
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'client' => $this->client->toArray(),
|
||||
'vendor' => $this->vendor->toArray(),
|
||||
'invoice' => $this->invoice->toArray(),
|
||||
'sales' => $this->sales->toArray(),
|
||||
'quote' => $this->quote->toArray(),
|
||||
'purchase_order' => $this->purchase_order->toArray(),
|
||||
'product' => $this->product->toArray(),
|
||||
'payment' => $this->payment->toArray(),
|
||||
'expense' => $this->expense->toArray(),
|
||||
'default_income_account' => $this->default_income_account,
|
||||
'default_expense_account' => $this->default_expense_account,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,11 @@ class QuickbooksSyncMap
|
||||
: SyncDirection::BIDIRECTIONAL;
|
||||
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'direction' => $this->direction->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,27 +21,27 @@ use Illuminate\Support\Facades\Route;
|
||||
Route::get('/', [BaseController::class, 'flutterRoute'])->middleware('guest');
|
||||
|
||||
Route::get('setup', [SetupController::class, 'index'])->middleware('guest');
|
||||
Route::post('setup', [SetupController::class, 'doSetup'])->middleware('guest');
|
||||
Route::get('update', [SetupController::class, 'update'])->middleware('guest');
|
||||
Route::post('setup', [SetupController::class, 'doSetup'])->throttle(10, 1)->middleware('guest');
|
||||
Route::get('update', [SetupController::class, 'update'])->throttle(10, 1)->middleware('guest');
|
||||
|
||||
Route::post('setup/check_db', [SetupController::class, 'checkDB'])->middleware('guest');
|
||||
Route::post('setup/check_mail', [SetupController::class, 'checkMail'])->middleware('guest');
|
||||
Route::post('setup/check_pdf', [SetupController::class, 'checkPdf'])->middleware('guest');
|
||||
Route::post('setup/check_db', [SetupController::class, 'checkDB'])->throttle(10, 1)->middleware('guest');
|
||||
Route::post('setup/check_mail', [SetupController::class, 'checkMail'])->throttle(10, 1)->middleware('guest');
|
||||
Route::post('setup/check_pdf', [SetupController::class, 'checkPdf'])->throttle(10, 1)->middleware('guest');
|
||||
|
||||
Route::get('password/reset', [ForgotPasswordController::class, 'showLinkRequestForm'])->middleware('domain_db')->name('password.request');
|
||||
Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
|
||||
Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->throttle(10, 1)->name('password.email');
|
||||
Route::get('password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])->middleware(['domain_db', 'email_db'])->name('password.reset');
|
||||
Route::post('password/reset', [ResetPasswordController::class, 'reset'])->middleware('email_db')->name('password.update');
|
||||
Route::post('password/reset', [ResetPasswordController::class, 'reset'])->throttle(10, 1)->middleware('email_db')->name('password.update');
|
||||
|
||||
Route::get('auth/{provider}', [LoginController::class, 'redirectToProvider']);
|
||||
|
||||
Route::middleware(['url_db'])->group(function () {
|
||||
Route::get('/user/confirm/{confirmation_code}', [UserController::class, 'confirm']);
|
||||
Route::post('/user/confirm/{confirmation_code}', [UserController::class, 'confirmWithPassword']);
|
||||
Route::get('/user/confirm/{confirmation_code}', [UserController::class, 'confirm'])->throttle(10, 1);
|
||||
Route::post('/user/confirm/{confirmation_code}', [UserController::class, 'confirmWithPassword'])->throttle(10, 1);
|
||||
});
|
||||
|
||||
Route::get('stripe/signup/{token}', [StripeConnectController::class, 'initialize'])->name('stripe_connect.initialization');
|
||||
Route::get('stripe/completed', [StripeConnectController::class, 'completed'])->name('stripe_connect.return');
|
||||
Route::get('stripe/signup/{token}', [StripeConnectController::class, 'initialize'])->throttle(10, 1)->name('stripe_connect.initialization');
|
||||
Route::get('stripe/completed', [StripeConnectController::class, 'completed'])->throttle(10, 1)->name('stripe_connect.return');
|
||||
|
||||
Route::get('yodlee/onboard/{token}', [YodleeController::class, 'auth'])->name('yodlee.auth');
|
||||
|
||||
|
||||
202
tests/Unit/QuickbooksSettingsSerializationComparisonTest.php
Normal file
202
tests/Unit/QuickbooksSettingsSerializationComparisonTest.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\DataMapper\QuickbooksSettings;
|
||||
use App\DataMapper\QuickbooksSyncMap;
|
||||
use App\Enum\SyncDirection;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Direct comparison test showing the serialization bug and fix.
|
||||
*
|
||||
* This test demonstrates:
|
||||
* 1. The old method (get_object_vars) fails to properly serialize nested objects and enums
|
||||
* 2. The new method (toArray) correctly serializes everything
|
||||
*/
|
||||
class QuickbooksSettingsSerializationComparisonTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test showing that get_object_vars() on an enum returns name/value array.
|
||||
*
|
||||
* While json_encode() handles this correctly, get_object_vars() on an enum
|
||||
* itself returns an array with 'name' and 'value' keys, not just the value.
|
||||
* This demonstrates why explicit toArray() is better for control.
|
||||
*/
|
||||
public function testGetObjectVarsOnEnumReturnsNameValueArray()
|
||||
{
|
||||
$syncMap = new QuickbooksSyncMap([
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
]);
|
||||
|
||||
// get_object_vars on the enum property itself
|
||||
$enumVars = get_object_vars($syncMap->direction);
|
||||
|
||||
// The enum's internal structure has both name and value
|
||||
$this->assertIsArray($enumVars);
|
||||
$this->assertArrayHasKey('name', $enumVars);
|
||||
$this->assertArrayHasKey('value', $enumVars);
|
||||
$this->assertEquals('PULL', $enumVars['name']);
|
||||
$this->assertEquals('pull', $enumVars['value']);
|
||||
|
||||
// While json_encode handles this, toArray() gives explicit control
|
||||
$array = $syncMap->toArray();
|
||||
$this->assertEquals('pull', $array['direction'],
|
||||
'toArray() explicitly returns just the value string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test showing that toArray() correctly serializes enums.
|
||||
*/
|
||||
public function testToArrayCorrectlySerializesEnums()
|
||||
{
|
||||
$syncMap = new QuickbooksSyncMap([
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
]);
|
||||
|
||||
// New method: toArray()
|
||||
$array = $syncMap->toArray();
|
||||
$json = json_encode($array);
|
||||
$decoded = json_decode($json, true);
|
||||
|
||||
// The enum IS properly serialized as a string value
|
||||
$this->assertIsString($decoded['direction'],
|
||||
'New method: enum is serialized as string');
|
||||
|
||||
// The decoded value IS the string 'pull'
|
||||
$this->assertEquals('pull', $decoded['direction'],
|
||||
'New method: enum value is correctly serialized as string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test showing that get_object_vars() relies on json_encode() for nested objects.
|
||||
*
|
||||
* While get_object_vars() + json_encode() works, it relies on PHP's automatic
|
||||
* serialization. The toArray() method provides explicit, controlled serialization
|
||||
* that's more maintainable and testable.
|
||||
*/
|
||||
public function testGetObjectVarsReliesOnJsonEncodeForNestedObjects()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'test_token',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Old method: get_object_vars (relies on json_encode to handle nested objects)
|
||||
$vars = get_object_vars($settings);
|
||||
$json = json_encode($vars);
|
||||
$decoded = json_decode($json, true);
|
||||
|
||||
// json_encode() does handle this correctly, but it's implicit
|
||||
$this->assertIsArray($decoded['settings'],
|
||||
'json_encode handles nested objects, but implicitly');
|
||||
|
||||
// The new method is explicit and controlled
|
||||
$array = $settings->toArray();
|
||||
$this->assertIsArray($array['settings'],
|
||||
'toArray() explicitly converts nested objects');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test showing that toArray() correctly serializes nested objects.
|
||||
*/
|
||||
public function testToArrayCorrectlySerializesNestedObjects()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'test_token',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
'invoice' => [
|
||||
'direction' => SyncDirection::PUSH->value,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// New method: toArray()
|
||||
$array = $settings->toArray();
|
||||
$json = json_encode($array);
|
||||
$decoded = json_decode($json, true);
|
||||
|
||||
// The nested QuickbooksSync object IS properly converted to an array
|
||||
$this->assertIsArray($decoded['settings'],
|
||||
'New method: nested object is converted to array');
|
||||
|
||||
// The nested QuickbooksSyncMap objects are also converted
|
||||
$this->assertIsArray($decoded['settings']['client'],
|
||||
'New method: nested sync map is converted to array');
|
||||
|
||||
// The enum values are properly serialized as strings
|
||||
$this->assertEquals('pull', $decoded['settings']['client']['direction'],
|
||||
'New method: nested enum is serialized as string');
|
||||
$this->assertEquals('push', $decoded['settings']['invoice']['direction'],
|
||||
'New method: nested enum is serialized as string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Side-by-side comparison: old vs new method.
|
||||
*
|
||||
* Both methods work, but toArray() provides:
|
||||
* 1. Explicit control over serialization
|
||||
* 2. Better maintainability
|
||||
* 3. Consistency with other DataMapper classes
|
||||
* 4. Easier testing and debugging
|
||||
*/
|
||||
public function testSideBySideComparison()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'token_123',
|
||||
'refresh_token' => 'refresh_456',
|
||||
'realmID' => 'realm_789',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::BIDIRECTIONAL->value,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// OLD METHOD (works but implicit)
|
||||
$oldVars = get_object_vars($settings);
|
||||
$oldJson = json_encode($oldVars);
|
||||
$oldDecoded = json_decode($oldJson, true);
|
||||
|
||||
// NEW METHOD (explicit and controlled)
|
||||
$newArray = $settings->toArray();
|
||||
$newJson = json_encode($newArray);
|
||||
$newDecoded = json_decode($newJson, true);
|
||||
|
||||
// Both methods produce valid results
|
||||
$this->assertEquals('token_123', $oldDecoded['accessTokenKey']);
|
||||
$this->assertEquals('token_123', $newDecoded['accessTokenKey']);
|
||||
|
||||
$this->assertEquals('bidirectional', $oldDecoded['settings']['client']['direction']);
|
||||
$this->assertEquals('bidirectional', $newDecoded['settings']['client']['direction']);
|
||||
|
||||
// Both produce equivalent results, but toArray() is explicit
|
||||
$this->assertEquals(
|
||||
json_encode($oldDecoded),
|
||||
json_encode($newDecoded),
|
||||
'Both methods produce equivalent results, but toArray() is explicit and maintainable'
|
||||
);
|
||||
|
||||
// The key difference: toArray() gives explicit control
|
||||
$this->assertIsArray($newArray, 'toArray() explicitly returns an array structure');
|
||||
$this->assertIsString($newArray['settings']['client']['direction'],
|
||||
'toArray() explicitly converts enum to string value');
|
||||
}
|
||||
}
|
||||
285
tests/Unit/QuickbooksSettingsSerializationTest.php
Normal file
285
tests/Unit/QuickbooksSettingsSerializationTest.php
Normal file
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Casts\QuickbooksSettingsCast;
|
||||
use App\DataMapper\QuickbooksSettings;
|
||||
use App\DataMapper\QuickbooksSync;
|
||||
use App\DataMapper\QuickbooksSyncMap;
|
||||
use App\Enum\SyncDirection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Test QuickbooksSettings serialization to verify the fix for enum and nested object serialization.
|
||||
*/
|
||||
class QuickbooksSettingsSerializationTest extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* Test that demonstrates toArray() provides explicit control over enum serialization.
|
||||
*
|
||||
* While get_object_vars() + json_encode() works (json_encode handles enums),
|
||||
* toArray() provides explicit, controlled serialization that's more maintainable.
|
||||
*/
|
||||
public function testToArrayProvidesExplicitEnumSerialization()
|
||||
{
|
||||
$syncMap = new QuickbooksSyncMap([
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
]);
|
||||
|
||||
// Using toArray() - explicit and controlled
|
||||
$array = $syncMap->toArray();
|
||||
$json = json_encode($array);
|
||||
$decoded = json_decode($json, true);
|
||||
|
||||
// Verify explicit serialization works correctly
|
||||
$this->assertIsString($decoded['direction']);
|
||||
$this->assertEquals('pull', $decoded['direction']);
|
||||
|
||||
// toArray() explicitly converts enum to string value
|
||||
$this->assertIsString($array['direction'],
|
||||
'toArray() explicitly returns enum as string value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the new toArray() method properly serializes enums.
|
||||
*/
|
||||
public function testNewSerializationMethodWorksWithEnums()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'test_token',
|
||||
'refresh_token' => 'refresh_token',
|
||||
'realmID' => '123456',
|
||||
'accessTokenExpiresAt' => 1234567890,
|
||||
'refreshTokenExpiresAt' => 1234567890,
|
||||
'baseURL' => 'https://sandbox-quickbooks.api.intuit.com',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
'invoice' => [
|
||||
'direction' => SyncDirection::PUSH->value,
|
||||
],
|
||||
'product' => [
|
||||
'direction' => SyncDirection::BIDIRECTIONAL->value,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Use the new toArray() method
|
||||
$array = $settings->toArray();
|
||||
$json = json_encode($array);
|
||||
$decoded = json_decode($json, true);
|
||||
|
||||
// Verify enum values are properly serialized as strings
|
||||
$this->assertIsString($decoded['settings']['client']['direction']);
|
||||
$this->assertEquals('pull', $decoded['settings']['client']['direction']);
|
||||
|
||||
$this->assertIsString($decoded['settings']['invoice']['direction']);
|
||||
$this->assertEquals('push', $decoded['settings']['invoice']['direction']);
|
||||
|
||||
$this->assertIsString($decoded['settings']['product']['direction']);
|
||||
$this->assertEquals('bidirectional', $decoded['settings']['product']['direction']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that nested objects are properly serialized.
|
||||
*/
|
||||
public function testNestedObjectsAreProperlySerialized()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'test_token',
|
||||
'refresh_token' => 'refresh_token',
|
||||
'realmID' => '123456',
|
||||
'accessTokenExpiresAt' => 1234567890,
|
||||
'refreshTokenExpiresAt' => 1234567890,
|
||||
'baseURL' => 'https://sandbox-quickbooks.api.intuit.com',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
'default_income_account' => 'income_account_123',
|
||||
'default_expense_account' => 'expense_account_456',
|
||||
],
|
||||
]);
|
||||
|
||||
$array = $settings->toArray();
|
||||
|
||||
// Verify nested QuickbooksSync structure
|
||||
$this->assertIsArray($array['settings']);
|
||||
$this->assertArrayHasKey('client', $array['settings']);
|
||||
$this->assertArrayHasKey('default_income_account', $array['settings']);
|
||||
$this->assertEquals('income_account_123', $array['settings']['default_income_account']);
|
||||
$this->assertEquals('expense_account_456', $array['settings']['default_expense_account']);
|
||||
|
||||
// Verify nested QuickbooksSyncMap structure
|
||||
$this->assertIsArray($array['settings']['client']);
|
||||
$this->assertArrayHasKey('direction', $array['settings']['client']);
|
||||
$this->assertEquals('pull', $array['settings']['client']['direction']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test round-trip serialization through the cast.
|
||||
*/
|
||||
public function testRoundTripSerializationThroughCast()
|
||||
{
|
||||
$originalSettings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'test_token_123',
|
||||
'refresh_token' => 'refresh_token_456',
|
||||
'realmID' => 'realm_789',
|
||||
'accessTokenExpiresAt' => 1234567890,
|
||||
'refreshTokenExpiresAt' => 9876543210,
|
||||
'baseURL' => 'https://sandbox-quickbooks.api.intuit.com',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
'invoice' => [
|
||||
'direction' => SyncDirection::PUSH->value,
|
||||
],
|
||||
'product' => [
|
||||
'direction' => SyncDirection::BIDIRECTIONAL->value,
|
||||
],
|
||||
'default_income_account' => 'income_123',
|
||||
'default_expense_account' => 'expense_456',
|
||||
],
|
||||
]);
|
||||
|
||||
$cast = new QuickbooksSettingsCast();
|
||||
|
||||
// Create a mock model for the cast
|
||||
$model = new class extends Model {
|
||||
// Empty model for testing
|
||||
};
|
||||
|
||||
// Serialize (set)
|
||||
$serialized = $cast->set($model, 'quickbooks', $originalSettings, []);
|
||||
|
||||
$this->assertNotNull($serialized);
|
||||
$this->assertIsString($serialized);
|
||||
|
||||
// Deserialize (get)
|
||||
$deserialized = $cast->get($model, 'quickbooks', $serialized, []);
|
||||
|
||||
$this->assertInstanceOf(QuickbooksSettings::class, $deserialized);
|
||||
|
||||
// Verify all properties are preserved
|
||||
$this->assertEquals($originalSettings->accessTokenKey, $deserialized->accessTokenKey);
|
||||
$this->assertEquals($originalSettings->refresh_token, $deserialized->refresh_token);
|
||||
$this->assertEquals($originalSettings->realmID, $deserialized->realmID);
|
||||
$this->assertEquals($originalSettings->accessTokenExpiresAt, $deserialized->accessTokenExpiresAt);
|
||||
$this->assertEquals($originalSettings->refreshTokenExpiresAt, $deserialized->refreshTokenExpiresAt);
|
||||
$this->assertEquals($originalSettings->baseURL, $deserialized->baseURL);
|
||||
|
||||
// Verify nested settings are preserved
|
||||
$this->assertInstanceOf(QuickbooksSync::class, $deserialized->settings);
|
||||
$this->assertEquals('income_123', $deserialized->settings->default_income_account);
|
||||
$this->assertEquals('expense_456', $deserialized->settings->default_expense_account);
|
||||
|
||||
// Verify enum values are preserved correctly
|
||||
$this->assertInstanceOf(QuickbooksSyncMap::class, $deserialized->settings->client);
|
||||
$this->assertEquals(SyncDirection::PULL, $deserialized->settings->client->direction);
|
||||
|
||||
$this->assertInstanceOf(QuickbooksSyncMap::class, $deserialized->settings->invoice);
|
||||
$this->assertEquals(SyncDirection::PUSH, $deserialized->settings->invoice->direction);
|
||||
|
||||
$this->assertInstanceOf(QuickbooksSyncMap::class, $deserialized->settings->product);
|
||||
$this->assertEquals(SyncDirection::BIDIRECTIONAL, $deserialized->settings->product->direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that all entity types are properly serialized.
|
||||
*/
|
||||
public function testAllEntityTypesAreSerialized()
|
||||
{
|
||||
$settings = new QuickbooksSettings([
|
||||
'settings' => [
|
||||
'client' => ['direction' => SyncDirection::PULL->value],
|
||||
'vendor' => ['direction' => SyncDirection::PUSH->value],
|
||||
'invoice' => ['direction' => SyncDirection::BIDIRECTIONAL->value],
|
||||
'sales' => ['direction' => SyncDirection::PULL->value],
|
||||
'quote' => ['direction' => SyncDirection::PUSH->value],
|
||||
'purchase_order' => ['direction' => SyncDirection::BIDIRECTIONAL->value],
|
||||
'product' => ['direction' => SyncDirection::PULL->value],
|
||||
'payment' => ['direction' => SyncDirection::PUSH->value],
|
||||
'expense' => ['direction' => SyncDirection::BIDIRECTIONAL->value],
|
||||
],
|
||||
]);
|
||||
|
||||
$array = $settings->toArray();
|
||||
|
||||
$entities = ['client', 'vendor', 'invoice', 'sales', 'quote', 'purchase_order', 'product', 'payment', 'expense'];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$this->assertArrayHasKey($entity, $array['settings'], "Entity {$entity} should be in serialized array");
|
||||
$this->assertArrayHasKey('direction', $array['settings'][$entity], "Entity {$entity} should have direction");
|
||||
$this->assertIsString($array['settings'][$entity]['direction'], "Entity {$entity} direction should be a string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that empty/default settings serialize correctly.
|
||||
*/
|
||||
public function testEmptySettingsSerializeCorrectly()
|
||||
{
|
||||
$settings = new QuickbooksSettings();
|
||||
|
||||
$array = $settings->toArray();
|
||||
|
||||
// Verify all OAuth fields have default values
|
||||
$this->assertEquals('', $array['accessTokenKey']);
|
||||
$this->assertEquals('', $array['refresh_token']);
|
||||
$this->assertEquals('', $array['realmID']);
|
||||
$this->assertEquals(0, $array['accessTokenExpiresAt']);
|
||||
$this->assertEquals(0, $array['refreshTokenExpiresAt']);
|
||||
$this->assertEquals('', $array['baseURL']);
|
||||
|
||||
// Verify settings structure exists
|
||||
$this->assertIsArray($array['settings']);
|
||||
$this->assertArrayHasKey('client', $array['settings']);
|
||||
|
||||
// Verify default direction is BIDIRECTIONAL
|
||||
$this->assertEquals('bidirectional', $array['settings']['client']['direction']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that JSON produced by toArray() can be decoded and reconstructed.
|
||||
*/
|
||||
public function testJsonCanBeDecodedAndReconstructed()
|
||||
{
|
||||
$originalSettings = new QuickbooksSettings([
|
||||
'accessTokenKey' => 'token_123',
|
||||
'settings' => [
|
||||
'client' => [
|
||||
'direction' => SyncDirection::PULL->value,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Serialize to JSON
|
||||
$json = json_encode($originalSettings->toArray());
|
||||
$this->assertIsString($json);
|
||||
|
||||
// Decode JSON
|
||||
$decoded = json_decode($json, true);
|
||||
$this->assertIsArray($decoded);
|
||||
|
||||
// Reconstruct from decoded array
|
||||
$reconstructed = QuickbooksSettings::fromArray($decoded);
|
||||
|
||||
// Verify reconstruction
|
||||
$this->assertEquals($originalSettings->accessTokenKey, $reconstructed->accessTokenKey);
|
||||
$this->assertEquals($originalSettings->settings->client->direction, $reconstructed->settings->client->direction);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user