diff --git a/inc/main.php b/inc/main.php index c0f9854b..23b16589 100644 --- a/inc/main.php +++ b/inc/main.php @@ -157,6 +157,8 @@ public static function register_conflicts( $conflicts_to_register = [] ) { * @return array */ public static function add_settings( $data ): array { + $data = is_array( $data ) ? $data : []; + $saved_data = ( new Optml_Settings() )->get_raw_settings(); unset( $saved_data['service_data'] ); unset( $saved_data['api_key'] ); diff --git a/inc/settings.php b/inc/settings.php index d0e410b6..1707afff 100644 --- a/inc/settings.php +++ b/inc/settings.php @@ -783,7 +783,9 @@ public function reset() { * @return array */ public function get_raw_settings() { - return get_option( $this->namespace, false ); + $raw_settings = get_option( $this->namespace, [] ); + + return is_array( $raw_settings ) ? $raw_settings : []; } diff --git a/tests/test-logger-data.php b/tests/test-logger-data.php new file mode 100644 index 00000000..5eb18841 --- /dev/null +++ b/tests/test-logger-data.php @@ -0,0 +1,84 @@ +get_raw_settings(); + + $this->assertIsArray( $raw_settings ); + $this->assertSame( [], $raw_settings ); + } + + /** + * Raw settings must be an array even when the option holds a non-array value. + */ + public function test_get_raw_settings_returns_array_when_option_is_not_an_array() { + update_option( OPTML_NAMESPACE . '_settings', 'corrupted' ); + + $this->assertSame( [], ( new Optml_Settings() )->get_raw_settings() ); + } + + /** + * `add_settings` must not fatal when there is nothing stored in the database. + */ + public function test_add_settings_with_missing_option() { + delete_option( OPTML_NAMESPACE . '_settings' ); + + $data = Optml_Main::add_settings( [ 'foo' => 'bar' ] ); + + $this->assertIsArray( $data ); + $this->assertSame( [ 'foo' => 'bar' ], $data ); + } + + /** + * Secrets are never sent along with the logger data. + */ + public function test_add_settings_strips_secrets() { + update_option( + OPTML_NAMESPACE . '_settings', + [ + 'api_key' => 'secret-key', + 'service_data' => [ 'cdn_key' => 'key', 'cdn_secret' => 'secret' ], + 'quality' => 'auto', + ] + ); + + $data = Optml_Main::add_settings( [] ); + + $this->assertArrayNotHasKey( 'api_key', $data ); + $this->assertArrayNotHasKey( 'service_data', $data ); + $this->assertSame( 'auto', $data['quality'] ); + } + + /** + * The logger cron collects its payload through this filter, it must survive a site + * where Optimole settings were never saved. + */ + public function test_logger_data_filter_without_stored_settings() { + delete_option( OPTML_NAMESPACE . '_settings' ); + + $this->assertNotFalse( has_filter( 'optimole_wp_logger_data', [ 'Optml_Main', 'add_settings' ] ) ); + $this->assertIsArray( apply_filters( 'optimole_wp_logger_data', [] ) ); + } +}