Skip to content

Commit 68a45d0

Browse files
authored
Merge pull request #10068 from magento-gl/spartans_pr_03092025
[Spartans] BugFixes Delivery
2 parents 527580b + 6638485 commit 68a45d0

File tree

26 files changed

+2750
-63
lines changed

26 files changed

+2750
-63
lines changed

app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,13 @@ public function execute(): Full
187187
protected function reindex(): void
188188
{
189189
$userFunctions = [];
190-
191190
foreach ($this->storeManager->getStores() as $store) {
192191
if ($this->getPathFromCategoryId($store->getRootCategoryId())) {
193192
$userFunctions[$store->getId()] = function () use ($store) {
194193
$this->reindexStore($store);
195194
};
196195
}
197196
}
198-
199197
$this->processManager->execute($userFunctions);
200198
}
201199

@@ -206,6 +204,9 @@ protected function reindex(): void
206204
*/
207205
private function reindexStore($store): void
208206
{
207+
// Ensure the same adapter instance that created the TEMP table is used for all operations
208+
// phpcs:ignore
209+
$this->connection = $this->tableMaintainer->getSameAdapterConnection();
209210
$this->reindexRootCategory($store);
210211
$this->reindexAnchorCategories($store);
211212
$this->reindexNonAnchorCategories($store);

app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ class TableMaintainer
3535

3636
/**
3737
* Catalog tmp category index table name
38+
*
39+
* @var string
3840
*/
3941
private $tmpTableSuffix = '_tmp';
4042

4143
/**
4244
* Catalog tmp category index table name
45+
*
46+
* @var string
4347
*/
4448
private $additionalTableSuffix = '_replica';
4549

@@ -73,6 +77,16 @@ private function getConnection()
7377
return $this->connection;
7478
}
7579

80+
/**
81+
* Expose connection so callers can use the same adapter instance that created temporary tables.
82+
*
83+
* @return AdapterInterface
84+
*/
85+
public function getSameAdapterConnection(): AdapterInterface
86+
{
87+
return $this->getConnection();
88+
}
89+
7690
/**
7791
* Return validated table name
7892
*
@@ -120,7 +134,7 @@ private function dropTable($tableName)
120134
/**
121135
* Return main index table name
122136
*
123-
* @param $storeId
137+
* @param int $storeId
124138
*
125139
* @return string
126140
*/
@@ -134,7 +148,7 @@ public function getMainTable(int $storeId)
134148
/**
135149
* Create main and replica index tables for store
136150
*
137-
* @param $storeId
151+
* @param int $storeId
138152
*
139153
* @return void
140154
*
@@ -161,7 +175,7 @@ public function createTablesForStore(int $storeId)
161175
/**
162176
* Drop main and replica index tables for store
163177
*
164-
* @param $storeId
178+
* @param int $storeId
165179
*
166180
* @return void
167181
*/
@@ -177,7 +191,7 @@ public function dropTablesForStore(int $storeId)
177191
/**
178192
* Return replica index table name
179193
*
180-
* @param $storeId
194+
* @param int $storeId
181195
*
182196
* @return string
183197
*/
@@ -189,7 +203,7 @@ public function getMainReplicaTable(int $storeId)
189203
/**
190204
* Create temporary index table for store
191205
*
192-
* @param $storeId
206+
* @param int $storeId
193207
*
194208
* @return void
195209
*/
@@ -206,7 +220,7 @@ public function createMainTmpTable(int $storeId)
206220
/**
207221
* Return temporary index table name
208222
*
209-
* @param $storeId
223+
* @param int $storeId
210224
*
211225
* @return string
212226
*
@@ -215,7 +229,7 @@ public function createMainTmpTable(int $storeId)
215229
public function getMainTmpTable(int $storeId)
216230
{
217231
if (!isset($this->mainTmpTable[$storeId])) {
218-
throw new \Magento\Framework\Exception\NoSuchEntityException('Temporary table does not exist');
232+
throw new \Magento\Framework\Exception\NoSuchEntityException(__('Temporary table does not exist'));
219233
}
220234
return $this->mainTmpTable[$storeId];
221235
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Catalog\Test\Unit\Model\Indexer\Category\Product;
10+
11+
use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction;
12+
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
13+
use Magento\Framework\App\ResourceConnection;
14+
use Magento\Framework\DB\Adapter\AdapterInterface;
15+
use Magento\Framework\DB\Ddl\Table as DdlTable;
16+
use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver as TableResolver;
17+
use Magento\Framework\Search\Request\Dimension;
18+
use PHPUnit\Framework\MockObject\MockObject;
19+
use PHPUnit\Framework\TestCase;
20+
21+
class TableMaintainerTest extends TestCase
22+
{
23+
/** @var ResourceConnection|MockObject */
24+
private $resource;
25+
26+
/** @var TableResolver|MockObject */
27+
private $tableResolver;
28+
29+
/** @var AdapterInterface|MockObject */
30+
private $adapter;
31+
32+
/** @var TableMaintainer */
33+
private $maintainer;
34+
35+
protected function setUp(): void
36+
{
37+
$this->resource = $this->createMock(ResourceConnection::class);
38+
$this->tableResolver = $this->createMock(TableResolver::class);
39+
$this->adapter = $this->createMock(AdapterInterface::class);
40+
41+
$this->resource->method('getConnection')->willReturn($this->adapter);
42+
$this->resource->method('getTableName')->willReturnCallback(
43+
static function (string $name) {
44+
return 'pref_' . $name;
45+
}
46+
);
47+
48+
$this->maintainer = new TableMaintainer($this->resource, $this->tableResolver);
49+
}
50+
51+
public function testGetMainTableAndReplicaTableName(): void
52+
{
53+
$storeId = 3;
54+
$resolvedMain = 'catalog_category_product_index_store3';
55+
56+
$this->tableResolver->expects($this->atLeastOnce())
57+
->method('resolve')
58+
->with(AbstractAction::MAIN_INDEX_TABLE, $this->callback(function (array $dims): bool {
59+
return isset($dims[0]) && $dims[0] instanceof Dimension;
60+
}))
61+
->willReturn($resolvedMain);
62+
63+
$this->assertSame($resolvedMain, $this->maintainer->getMainTable($storeId));
64+
$this->assertSame($resolvedMain . '_replica', $this->maintainer->getMainReplicaTable($storeId));
65+
}
66+
67+
public function testCreateMainTmpTableAndGetMainTmpTable(): void
68+
{
69+
$storeId = 7;
70+
$resolvedMain = 'index_7';
71+
$tmpName = $resolvedMain . '_tmp';
72+
73+
$this->tableResolver->expects($this->any())
74+
->method('resolve')
75+
->willReturn($resolvedMain);
76+
77+
$this->adapter->expects($this->once())
78+
->method('createTemporaryTableLike')
79+
->with($tmpName, $resolvedMain, true);
80+
81+
// First call creates and caches temp table
82+
$this->maintainer->createMainTmpTable($storeId);
83+
// Second call should be a no-op (still once)
84+
$this->maintainer->createMainTmpTable($storeId);
85+
86+
self::assertSame($tmpName, $this->maintainer->getMainTmpTable($storeId));
87+
88+
$this->expectException(\Magento\Framework\Exception\NoSuchEntityException::class);
89+
// Different store id should not have tmp table created
90+
$this->maintainer->getMainTmpTable($storeId + 1);
91+
}
92+
93+
public function testGetSameAdapterConnectionReturnsSameInstance(): void
94+
{
95+
$same = $this->maintainer->getSameAdapterConnection();
96+
$this->assertSame($this->adapter, $same);
97+
}
98+
99+
public function testCreateTablesForStoreCreatesMainAndReplicaWhenMissing(): void
100+
{
101+
$storeId = 2;
102+
$resolvedMain = 'index_2';
103+
$baseReplica = 'pref_' . AbstractAction::MAIN_INDEX_TABLE . '_replica';
104+
105+
$this->tableResolver->expects($this->any())
106+
->method('resolve')
107+
->willReturn($resolvedMain);
108+
109+
$expectedIsTableArgs = [$resolvedMain, $resolvedMain . '_replica'];
110+
$this->adapter->expects($this->exactly(2))
111+
->method('isTableExists')
112+
->willReturnCallback(function ($arg) use (&$expectedIsTableArgs) {
113+
$expected = array_shift($expectedIsTableArgs);
114+
\PHPUnit\Framework\Assert::assertSame($expected, $arg);
115+
return false;
116+
});
117+
118+
$ddlMain = $this->createMock(DdlTable::class);
119+
$ddlReplica = $this->createMock(DdlTable::class);
120+
121+
$createByDdlCall = 0;
122+
$this->adapter->expects($this->exactly(2))
123+
->method('createTableByDdl')
124+
->willReturnCallback(function (
125+
$base,
126+
$new
127+
) use (
128+
$baseReplica,
129+
$resolvedMain,
130+
$ddlMain,
131+
$ddlReplica,
132+
&$createByDdlCall
133+
) {
134+
if ($createByDdlCall === 0) {
135+
\PHPUnit\Framework\Assert::assertSame($baseReplica, $base);
136+
\PHPUnit\Framework\Assert::assertSame($resolvedMain, $new);
137+
$createByDdlCall++;
138+
return $ddlMain;
139+
}
140+
\PHPUnit\Framework\Assert::assertSame($baseReplica, $base);
141+
\PHPUnit\Framework\Assert::assertSame($resolvedMain . '_replica', $new);
142+
$createByDdlCall++;
143+
return $ddlReplica;
144+
});
145+
146+
$expectedCreateArgs = [$ddlMain, $ddlReplica];
147+
$this->adapter->expects($this->exactly(2))
148+
->method('createTable')
149+
->willReturnCallback(function ($ddl) use (&$expectedCreateArgs) {
150+
$expected = array_shift($expectedCreateArgs);
151+
\PHPUnit\Framework\Assert::assertSame($expected, $ddl);
152+
return null;
153+
});
154+
155+
$this->maintainer->createTablesForStore($storeId);
156+
}
157+
158+
public function testDropTablesForStoreDropsWhenExists(): void
159+
{
160+
$storeId = 4;
161+
$resolvedMain = 'index_4';
162+
163+
$this->tableResolver->expects($this->any())
164+
->method('resolve')
165+
->willReturn($resolvedMain);
166+
167+
$expectedIsTableArgs = [$resolvedMain, $resolvedMain . '_replica'];
168+
$this->adapter->expects($this->exactly(2))
169+
->method('isTableExists')
170+
->willReturnCallback(function ($arg) use (&$expectedIsTableArgs) {
171+
$expected = array_shift($expectedIsTableArgs);
172+
\PHPUnit\Framework\Assert::assertSame($expected, $arg);
173+
return true;
174+
});
175+
176+
$expectedDropArgs = [$resolvedMain, $resolvedMain . '_replica'];
177+
$this->adapter->expects($this->exactly(2))
178+
->method('dropTable')
179+
->willReturnCallback(function ($arg) use (&$expectedDropArgs) {
180+
$expected = array_shift($expectedDropArgs);
181+
\PHPUnit\Framework\Assert::assertSame($expected, $arg);
182+
return null;
183+
});
184+
185+
$this->maintainer->dropTablesForStore($storeId);
186+
}
187+
}

app/code/Magento/Customer/Controller/Account/CreatePost.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2014 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -226,6 +226,7 @@ public function __construct(
226226
* Retrieve cookie manager
227227
*
228228
* @deprecated 100.1.0
229+
* @see https://jira.corp.magento.com/browse/MAGETWO-71174
229230
* @return \Magento\Framework\Stdlib\Cookie\PhpCookieManager
230231
*/
231232
private function getCookieManager()
@@ -242,6 +243,7 @@ private function getCookieManager()
242243
* Retrieve cookie metadata factory
243244
*
244245
* @deprecated 100.1.0
246+
* @see https://jira.corp.magento.com/browse/MAGETWO-71174
245247
* @return \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory
246248
*/
247249
private function getCookieMetadataFactory()
@@ -361,6 +363,8 @@ public function execute()
361363
return $this->resultRedirectFactory->create()
362364
->setUrl($this->_redirect->error($url));
363365
}
366+
367+
$this->decodePunycodeEmail();
364368
$this->session->regenerateId();
365369
try {
366370
$address = $this->extractAddress();
@@ -512,4 +516,28 @@ private function getMessageManagerSuccessMessage(): MessageInterface
512516

513517
return $message;
514518
}
519+
520+
/**
521+
* Convert punycode email back to Unicode
522+
*
523+
* @return void
524+
*/
525+
private function decodePunycodeEmail(): void
526+
{
527+
$email = $this->getRequest()->getParam('email');
528+
if (!$email || strpos($email, '@') === false) {
529+
return;
530+
}
531+
532+
// Split local part and domain
533+
[$localPart, $domain] = explode('@', $email, 2);
534+
535+
// Only decode if domain contains punycode (contains 'xn--')
536+
if (function_exists('idn_to_utf8') && strpos($domain, 'xn--') !== false) {
537+
$decodedDomain = idn_to_utf8($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46);
538+
if ($decodedDomain !== false && $decodedDomain !== $domain) {
539+
$this->getRequest()->setParam('email', $localPart . '@' . $decodedDomain);
540+
}
541+
}
542+
}
515543
}

0 commit comments

Comments
 (0)