1

I have a WP "unit" test, run in multisite mode, that looks like this:

class EffectsTest extends WP_UnitTestCase {
  public function test_wp_insert_site() {
    $id = wp_insert_site([
      'title'      => 'My Site Title',
      'domain'     => 'foo.multicare.org',
      'meta'       => [
        'whatever' => 'whatever',
      ],
    ]);

    $this->assertNotEmpty($id);
  }
}

The test passes but it's triggering the error:

WordPress database error Can't reopen table: 'a' for query DELETE a, b FROM wptests_2_options a, wptests_2_options b
                        WHERE a.option_name LIKE '\\_transient\\_%'
                        AND a.option_name NOT LIKE '\\_transient\\_timeout\\_%'
                        AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
                        AND b.option_value < 1605216174
made by PHPUnit\TextUI\Command::main, PHPUnit\TextUI\Command->run, PHPUnit\TextUI\TestRunner->doRun,
PHPUnit\Framework\TestSuite->run, PHPUnit\Framework\TestSuite->run, PHPUnit\Framework\TestSuite->run,
PHPUnit\Framework\TestCase->run, PHPUnit\Framework\TestResult->run, PHPUnit\Framework\TestCase->runBare,
PHPUnit\Framework\TestCase->runTest, MultiCare\Integration\EffectsTest->test_wp_insert_site, 
wp_insert_site, do_action('wp_initialize_site'), WP_Hook->do_action, WP_Hook->apply_filters, 
wp_initialize_site, populate_options, delete_expired_transients

The actual error originates in delete_expired_transients(), which appears to be deleting from two aliases to the same table, $wpdb->options:

 "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b..."

MySQL doesn't seem to like that. So my question is, is there a way to prevent this without patching WP Core? Seems like a core bug to me, but perhaps I'm missing something.

(The actual test calls a function from my application code, so I'm not just testing wp_insert_site(). But that's irrelevant here because it's the call to wp_insert_site() that's triggering the error.)

6
  • I don't know, but I do see you're PHP serialising 2 values into a single post meta, it would be more secure and flexible t just use 2 meta keys, e.g. mc_central_data_id and mc_central_data_site
    – Tom J Nowell
    Nov 12, 2020 at 22:04
  • @TomJNowell more secure how?
    – acobster
    Nov 12, 2020 at 23:06
  • To be clear, that is trusted data. It's not actually user input. And we do want it to be open/dynamic for reasons that are not really relevant here; we won't be querying by those nested fields.
    – acobster
    Nov 12, 2020 at 23:09
  • PHP serialized data leaves you open to deserialisation attacks, there are no advantages here to serializing, it is unnecessary, adds complexity, and adds an attack vector. Serialising provides no advantages that cannot be gotten by other means
    – Tom J Nowell
    Nov 13, 2020 at 2:25
  • By that token, the auto-serialization in update_post_meta() is never advantageous and is always less secure, so you should just never rely on array meta fields, is that right?
    – acobster
    Nov 15, 2020 at 8:43

1 Answer 1

1

I've just faced the same issue, and the solution was calling WP_UnitTest_Factory_For_Blog::create instead. In OP's case it would become:

class EffectsTest extends WP_UnitTestCase {
  public function test_wp_insert_site() {
    $factory = new \WP_UnitTest_Factory_For_Blog();
    $id      = $site_factory->create(
      [
        'title'      => 'My Site Title',
        'domain'     => 'foo.multicare.org',
        'meta'       => [
          'whatever' => 'whatever',
        ],
      ]
    );

    $this->assertNotEmpty($id);
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.