From ef267c4ae3bca17d1a9ee1e09d6d422e59cc1eaf Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot <> Date: Mon, 2 Oct 2023 23:21:30 +0000 Subject: [PATCH] chore: Build release --- .gitattributes | 3 - .github/workflows/pack-plugin.yml | 15 - .github/workflows/test.yml | 54 --- .gitignore | 6 - .shopware-extension.yml | 58 --- .../frosh-platform-thumbnail-processor.css | 1 + .../js/frosh-platform-thumbnail-processor.js | 37 ++ src/Resources/store/de.md | 38 -- src/Resources/store/en.md | 36 -- src/Resources/store/icon.png | Bin 10151 -> 0 bytes src/Resources/store/images/0.jpg | Bin 35326 -> 0 bytes src/Resources/store/images/1.jpg | Bin 75185 -> 0 bytes tests/TestBootstraper.php | 46 -- tests/integration/MediaUrlTest.php | 176 -------- .../Controller/Api/TestControllerTest.php | 399 ------------------ .../ThumbnailSizesChangedListenerTest.php | 88 ---- .../FroshPlatformThumbnailProcessorTest.php | 27 -- ...Migration1686772873AddActiveConfigTest.php | 110 ----- tests/unit/Service/ConfigReaderTest.php | 139 ------ .../Service/SalesChannelIdDetectorTest.php | 116 ----- .../unit/Service/ThumbnailUrlTemplateTest.php | 73 ---- .../Service/UrlGeneratorDecoratorTest.php | 383 ----------------- 22 files changed, 38 insertions(+), 1767 deletions(-) delete mode 100644 .gitattributes delete mode 100644 .github/workflows/pack-plugin.yml delete mode 100644 .github/workflows/test.yml delete mode 100755 .gitignore delete mode 100644 .shopware-extension.yml create mode 100644 src/Resources/public/administration/css/frosh-platform-thumbnail-processor.css create mode 100644 src/Resources/public/administration/js/frosh-platform-thumbnail-processor.js delete mode 100644 src/Resources/store/de.md delete mode 100644 src/Resources/store/en.md delete mode 100644 src/Resources/store/icon.png delete mode 100644 src/Resources/store/images/0.jpg delete mode 100644 src/Resources/store/images/1.jpg delete mode 100644 tests/TestBootstraper.php delete mode 100644 tests/integration/MediaUrlTest.php delete mode 100644 tests/unit/Controller/Api/TestControllerTest.php delete mode 100644 tests/unit/EventListener/ThumbnailSizesChangedListenerTest.php delete mode 100644 tests/unit/FroshPlatformThumbnailProcessorTest.php delete mode 100644 tests/unit/Migration/Migration1686772873AddActiveConfigTest.php delete mode 100644 tests/unit/Service/ConfigReaderTest.php delete mode 100644 tests/unit/Service/SalesChannelIdDetectorTest.php delete mode 100644 tests/unit/Service/ThumbnailUrlTemplateTest.php delete mode 100644 tests/unit/Service/UrlGeneratorDecoratorTest.php diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index ceb9522..0000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -bin/ export-ignore -phpstan.neon.dist export-ignore -psalm.xml export-ignore diff --git a/.github/workflows/pack-plugin.yml b/.github/workflows/pack-plugin.yml deleted file mode 100644 index ea0c36e..0000000 --- a/.github/workflows/pack-plugin.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: PackPlugin -on: - push: - branches: - - main - -jobs: - build: - uses: FriendsOfShopware/actions/.github/workflows/store-shopware-cli.yml@main - with: - extensionName: FroshPlatformThumbnailProcessor - secrets: - accountUser: ${{ secrets.ACCOUNT_USER }} - accountPassword: ${{ secrets.ACCOUNT_PASSWORD }} - ghToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 6c2d117..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Test -on: - push: - branches: - - 'main' - tags-ignore: - - '*' - pull_request: - schedule: - - cron: "5 15 * * *" - -jobs: - test: - strategy: - fail-fast: false - matrix: - version: [ "v6.5.0", "v6.5.1", "v6.5.2", "v6.5.3", "v6.5.4", "v6.5.5", "trunk" ] - container: ghcr.io/friendsofshopware/platform-plugin-dev:${{ matrix.version }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Prepare - run: | - start-mysql - mysql -V - php -v - cp -r "./" "/plugins/FroshPlatformThumbnailProcessor" - cd /plugins/FroshPlatformThumbnailProcessor - - - name: PHPUnit - run: | - cd /plugins/FroshPlatformThumbnailProcessor - php -d pcov.enabled=1 /opt/shopware/vendor/bin/phpunit --coverage-clover clover.xml - - - uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - files: ./clover.xml - root_dir: /plugins/FroshPlatformThumbnailProcessor - working-directory: /plugins/FroshPlatformThumbnailProcessor - - - name: Install specific dependencies for PHPStan at v6.5.0 - if: matrix.version == 'v6.5.0' - run: | - composer require tomasvotruba/type-coverage --dev --no-scripts -d /opt/shopware/ - - # we call this after PHPUnit to make sure that the generated files are available - - name: PHPStan - run: | - cd /plugins/FroshPlatformThumbnailProcessor - /opt/shopware/vendor/bin/phpstan analyse -c phpstan.neon diff --git a/.gitignore b/.gitignore deleted file mode 100755 index 751b333..0000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -vendor -frosh-platform-thumbnail-processor.css -frosh-platform-thumbnail-processor.js -node_modules -src/DependencyInjection/ThumbnailService.php -src/DependencyInjection/FileSaver.php diff --git a/.shopware-extension.yml b/.shopware-extension.yml deleted file mode 100644 index 80a065d..0000000 --- a/.shopware-extension.yml +++ /dev/null @@ -1,58 +0,0 @@ -store: - availabilities: - - German - - International - default_locale: en_GB - localizations: - - de_DE - - en_GB - categories: - - ConversionOptimierung - type: extension - icon: src/Resources/store/icon.png - automatic_bugfix_version_compatibility: false - description: - de: file:src/Resources/store/de.md - en: file:src/Resources/store/en.md - tags: - de: - - thumbnail - - performance - - storage - - webp - en: - - thumbnail - - performance - - storage - - webp - features: - de: - - Keine Thumbnail-Dateien mehr - - URL pro SalesChannel möglich - en: - - No more thumbnail files - - URL per SalesChannel possible - images: - - file: src/Resources/store/images/0.jpg - activate: - de: true - en: true - preview: - de: true - en: true - priority: 0 - - file: src/Resources/store/images/1.jpg - activate: - de: true - en: true - preview: - de: false - en: false - priority: 1 -build: - zip: - assets: - enable_es_build_for_admin: true - enable_es_build_for_storefront: true - - diff --git a/src/Resources/public/administration/css/frosh-platform-thumbnail-processor.css b/src/Resources/public/administration/css/frosh-platform-thumbnail-processor.css new file mode 100644 index 0000000..bdd8c0d --- /dev/null +++ b/src/Resources/public/administration/css/frosh-platform-thumbnail-processor.css @@ -0,0 +1 @@ +.sw-system-config .frosh-thumbnail-processor-info-texts a,.sw-system-config .frosh-thumbnail-processor-info-texts a:visited{text-decoration:none;color:#189eff}.sw-system-config .frosh-thumbnail-processor-info-texts p{margin:revert}.frosh-thumbnail-processor .testdata-container{margin:20px 0}#test-result{margin:10px 0} diff --git a/src/Resources/public/administration/js/frosh-platform-thumbnail-processor.js b/src/Resources/public/administration/js/frosh-platform-thumbnail-processor.js new file mode 100644 index 0000000..7f37a6a --- /dev/null +++ b/src/Resources/public/administration/js/frosh-platform-thumbnail-processor.js @@ -0,0 +1,37 @@ +(()=>{var a=`{% block frosh_thumbnail_processor_info_texts %} +
+

+ Hint: You can set the configuration per SalesChannel and you don't need to use all variables. +

+ +

+ Available variables for the ThumbnailPattern:
+ {mediaUrl}: e.g. https://cdn.test.de/
+ {mediaPath}: e.g. media/image/5b/6d/16/tea.png
+ {width}: e.g. 800 +

+ +

+ Find patterns in the discussion category 'Patterns' of the GitHub repository:
+ + GitHub Category 'Patterns' + +

+
+{% endblock %} +`;var{Component:p}=Shopware;p.register("frosh-thumbnail-processor-info-texts",{template:a});var c=` + + {{ btnLabel }} + + +
+

+
+
+
+`;var{Component:f,Mixin:d}=Shopware;f.register("thumbnailprocessor-test",{template:c,props:["btnLabel"],inject:["thumbnailProcessorTest"],mixins:[d.getByName("notification")],data(){return{isLoading:!1,isSuccessful:!1}},computed:{pluginSalesChannelId(){let t=this.getParentComponent();if(!t)throw"Can not get pluginConfigData";return t.currentSalesChannelId}},methods:{finish(){this.isSuccessful=!1},showError(t,s,e){this.isSuccessful=!1,s&&(t+=" sample url: "+s),e.innerText=t,e.scrollIntoView(),this.createNotificationError({title:this.$tc("thumbnail-processor.test.title"),message:t})},check(){this.isLoading=!0,this.thumbnailProcessorTest.getUrl(this.pluginSalesChannelId).then(t=>{if(t.url){this.isSuccessful=!0;let s=this,e=document.createElement("img"),r=document.querySelector("#testimage-container"),i=r.querySelector("img"),n=document.querySelector("#test-result");if(e.src=t.url,e.width=200,e.height=200,e.onload=function(){e.naturalWidth!==200&&s.showError(s.$tc("thumbnail-processor.test.error.noResize"),t.url,n)},e.onerror=function(){i.height=0,i.width=0,s.showError(s.$tc("thumbnail-processor.test.error.general"),t.url,n)},i){i.replaceWith(e);return}r.appendChild(e)}else this.showError(this.$tc("thumbnail-processor.test.error.general"));setTimeout(()=>{this.isLoading=!1},2500)})},systemConfigSaveAll(){this.isLoading=!0;let t=this.getParentComponent();if(!t)throw this.isLoading=!1,"Can not get systemConfig";t.saveAll().then(()=>{this.check(),this.isLoading=!1})},getParentComponent(t=this){return typeof t.actualConfigData<"u"?t:t.$parent?this.getParentComponent(t.$parent):null}}});var l=Shopware.Classes.ApiService,{Application:h}=Shopware,o=class extends l{constructor(s,e,r="thumbnail-processor-test"){super(s,e,r)}getUrl(s){let e=this.getBasicHeaders({});return this.httpClient.post(`_action/${this.getApiBasePath()}/get-sample-image`,{salesChannelId:s},{headers:e}).then(r=>l.handleResponse(r))}};h.addServiceProvider("thumbnailProcessorTest",t=>{let s=h.getContainer("init");return new o(s.httpClient,t.loginService)});})(); diff --git a/src/Resources/store/de.md b/src/Resources/store/de.md deleted file mode 100644 index c9291fd..0000000 --- a/src/Resources/store/de.md +++ /dev/null @@ -1,38 +0,0 @@ -Verschwende keine Rechenleistung und keinen Speicherplatz mit Thumbnails! -Mit diesem Plugin können Sie auf dynamische Thumbnails zurückgreifen. -Jedes Produkt, jedes Bild in der Einkaufswelten hat berechtigterweise Thumbnails. Diese werden standardmäßig -automatisch beim Upload erzeugt und auf der Festplatte gespeichert. -An diesem Punkt greift dieses Plugin ein und stellt die Funktion zur Verfügung, dass keine Thumbnaildateien mehr erstellt -werden müssen. Es bleibt lediglich, dass die Thumbnail-Größen in der Datenbank gespeichert werden. -Die Thumbnails werden dann in Echtzeit beim Besuch durch einen externen Dienst erzeugt und ausgeliefert. - -## Vorteile vom Sparen der Thumbnailerzeugung: -- Nutze einen externen Dienst, um deine Bilder optimiert auszuliefern -- Sparen von Speicherplatz -- Schnellerer Upload von Bildern -- Entlastung des Servers -- Schnellere Backups durch weniger Dateien - -## Hinzufügen weiterer Thumbnail-Größen: -- im Order der Medienverwaltung neue Größe hinterlegen -- dann den Befehl `bin/console media:generate-thumbnails` auf der Konsole ausführen, damit die Thumbnails für alle Bilder in der Datenbank aktualisiert werden (ab v3.0.2 nicht mehr notwendig) -- Shop-Cache leeren - -## Welchen Dienst verwende ich nun für die Thumbnails: -Beachten Sie, dass dieses Plugin nur die Funktion zum Ausliefern der Thumbnail-Urls bereitstellt. -Dieses Plugin erstellt keine Thumbnails! Dazu werden die entsprechenden Dienste verwendet. - -Es gibt drei Parameter, die Ihnen optional zur Erstellung des Links zur Verfügung stehen: -{mediaUrl}: Primär die Config shopware.cdn.url, alternativ Shop-Url -{mediaPath}: relativer Pfad zu dem Originalbild -{width}: Breite des Thumbnails - -Finde fertige Template auf GitHub: -[GitHub Category Patterns](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor/discussions/categories/patterns). - -Nach der Einrichtung und Prüfung im DeveloperTools Ihres Browsers, können Sie auch den vorhandenen thumbnail-Ordner sichern und löschen. - -Dieses Plugin wird von [@FriendsOfShopware](https://store.shopware.com/friends-of-shopware.html) entwickelt. -Maintainer dieses Plugins ist: [Sebastian König (tinect)](https://github.com/tinect) - -Bei Fragen / Fehlern bitte ein [GitHub Issue](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor/issues/new) erstellen diff --git a/src/Resources/store/en.md b/src/Resources/store/en.md deleted file mode 100644 index f10b158..0000000 --- a/src/Resources/store/en.md +++ /dev/null @@ -1,36 +0,0 @@ -Don't waste the computing power and space with thumbnails! With this plugin you can access dynamic thumbnails. -Every product, every picture in the shopping worlds legitimately has thumbnails. These become standard -generated and saved automatically during upload. -At this point this plugin intervenes and provides the function that no more thumbnail files are created. The only thing that remains is that the thumbnail sizes are stored in the database. -The thumbnails are then generated and delivered by an external service in real time when visiting. - -## Advantages of saving thumbnail generation: -- Use external service to deliver images optimized -- Save space on disk -- Faster upload of images -- Relief of the server -- Faster backups with fewer files - -## Adding more thumbnail sizes: -- Save new size in the folder of the media management -- then run the command `bin/console media:generate-thumbnails` on the console to update the thumbnails for all images in the database (no more needed from version 3.0.2) -- Clear shop cache - -## Which service do I now use for the thumbnails: -Please note that this plugin only provides the function for delivering the thumbnail urls. -This plugin does not create thumbnails! The corresponding services are used for this. - -There are three parameters that are optionally available for creating the link: -{mediaUrl}: Primarily your config shopware.cdn.url, alternatively shop url -{mediaPath}: The relative path to the original image -{width}: The width of the thumbnail - -Find pattern at GitHub: -[GitHub Category Patterns](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor/discussions/categories/patterns). - -After setting up and checking in the developer tools of your browser, you can also backup and delete the existing thumbnail folder. - -This plugin is part of [@FriendsOfShopware](https://store.shopware.com/en/friends-of-shopware.html). -Maintainer from the plugin is: [Sebastian König (tinect)](https://github.com/tinect) - -For questions or bugs please create a [GitHub Issue](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor/issues/new) diff --git a/src/Resources/store/icon.png b/src/Resources/store/icon.png deleted file mode 100644 index 885856b6e391cc84d714204bf9a596ca42f432b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10151 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%0A#QM*K|#aH?0 z+=VGn9(9F+Q28$J_(7j@R$QY(RCq%*I?vLagUdLfv#z=q^#rjn%@XxM*GwbUL-zt@ zmx_GDqhJ1(IIwZf3Z$CqBSOEV?EE7YGG{n&-0bIW&6>-+BkxfY;f*e`4>PP_DrATj z5z#4OK=t%5Q2rx_g%Cy=3Op|}~*Luj(YA*SZA z9gA)_REPTM&R9dg*+@kqsJ+E(DZIWKqSg|@5 zkE*SF*>h%bQJuq+eK={uVTrtf;i&aSbsFA}pHin!{`v-i3owI^MxrP|~=P&YBZ zryi>Ec@LD3yF^E87@LtJVKaTuok-Bcgl z4ShJ4zq8wz*fV8FjI+%1Q-)HlZ zH2lant*bC$iJFd*pW%?O>Elv#wHXaBV>?dqtQSGit7_2tB6rkeZhJ$U?1Wo?B%5^V ze#B>crcO&EiaU=z49}gtNQR-t3>=C_3UR5$@K>J&WRxe=0t~p4<}N$vYR-uR>!=hm zy|NkYlsoF9P)M|Emk<{M{PzxGG=3B62B>?5;z#yYr)+8+;awIS5;tc)@j0j)wD4Q*lpN$VEDN%O$T^yQoH5v}7Syhb`dj!4lsm1Nggxq3;H zM^HJ@d1qj-GgIc(eDagfUf1UKO=TCB`)QQ1n!-N1PBmVc{hg?|yJ>Z~>C>d}w#Fzx zP@2hL8Kods^*yTSyK+?*tmpCFK`1RnnJkIH1>fo{&9skYocl)uvn^AUVhu@Li_*`J z1z=8tPtG)I7rWCTCe3WQN-V$LQ)ODrWV{C+wBAZu6W9E>@-IHL36uGUo#n^h!96#2 zcf3w~QaX{DZ-#1Kah<>Dr|;`96Zghvj$(dP8@vuV_G2pJ+jYA7qyHW49}~Q7wGxN_{%0!QK=IV)%kK9~#XThafbE)%(%$tC>Fj6FpWg<(|neKbf3`U4AbE*zVR_jeO63gch4Kpm~l zGHec37;$yCVEjSnf(cbS8;ze8IE`wWjVKl>v#4m3o;&`FxGeF%0lO|so=UatNVplRb8-7 zCHfNu6<<9bx4vt_Sli>QdxBizD{&#pL|~dXhviwp0v#EXW9h6aFm_YMjk9tQgDcl*R7A-sKJPaw1~!|b zvQc0)Cb?+m`WY4l3;Rbg<8uy`Z~0#mYW{Rn-%fgjAzlGd6tPrwo3vBp4hV{gDs)vn zb6f`du<%9W9u_lHXQd{$ipXVx{_!+IBMUaR_ZE06E)_Dxc-y;Uu2ak<1bc*&2&XcJz&y?M9TZF7!^y)ps#W43y?|EaT46K=pAV# z+56C3b!W`-qo`-Y5~Q$(mN zGhJ=kG%j_nU-z2Ot@{{8eGEg)vf?@>8|y9su~5pkkx4*9g$V_;lFos8@WZwdI;#cN z21GQbbWbXm2I^hyWO??`DitstpV#1b#auUnu?~=8WN(&(!7#&nEE5xhVG3xh z`(a`dfpW?}%!_2paF<>FfzN{*57Np4+T}8C)|Xmap^IU0RY}D7k{cWK&&IBoU$E_4 z^^}`)9)&I;Q_|`>J0@6UXY`W5m5p7-i;P@iSW$19uU1NPetPP!6Kdqho^Qr?vtz)6 z$tMplR^hz2*ZeW`aA$igW1Wg_)vKU;a=!jvORG-}v}L$Vjo_E<@g%@TCnLfJH1X96 zi<`jAFVJe+`c;>fWnC}}d~#-qOhjxPDK0O2f}JJB&;L^72Xc{j>wj+lQ7Fijt{xRR z+O~tn^02CRVgTE0t%ZEU8!{0E5Nx zglO>IkLMa#t_$q&=fyia%y^<0Y#Sb(va*KT>*aVgdEEqZB5W9KTt;e**}N#LD%;?M z8?Ct2m0JLgj~+xcYfeaxg>pyo)MBVts$}_v{BSQ$P~JG;YvMO#EY{Dvra8Br1wrBDAZOjnr}bvy=BAtIr5bZ>Ch+F1 zE>MHmlUyQ^Xmhq~znmKd_cLH5>&~;O{xrbI)M;7uYd% zD?^(TnsOV*v_Q847ujed2JsZU)ROnXo#5rOX+i4Ue)U$NuOT6k6E;NO4kN6ft($Z( zlWC6*j;ndYR(u;Qh^&9w{<0%Lq`K$saH|O_#0fa?SIodec?A@E(Vq$Jhm}b8RoUL_ z?7z2@I()~bo#M8VFMSbu=5HEpLI3e(6Tv<%<%jsRr%&mk|A2qIQ)l>_qX1uP@3Nap zA;nMKW=g;w-G3@4GeT_yYRZhWSbqmjFi0&KPRi5Iu*kMAI{=d;c?I1^P@F0-{$Swf zSF)I$A?Ql&C9{iwX@F(5W4%higZ zEctm8MULmZ!kWf_0)4FA+K#gOis0f1N4G>R<1#e}fm%a3Zs5j`ZI+*U?X1eIgRu=q z%^4Kn^aY}wB+rca2f(^^f~Zp>PRZ1!F_`yDEhHF}{i~cfTcO(^El5lrWk2nS+TpqI zV_q@J)d&r2vD(@64p|#^nxGk_L+E#<>RBzm@=ZiV=jf%#>qU4esS*iv`cn3Cv$aFr zrL=R(r*^#@ad&YtS~CL`A5Qb3PVNq;AClYM^yYS@rkgzAU_#oYs*U;U2pN%xJRR$* zS#x!)A1am((p47sLn%fIO(~OJp_xdeWZV!MmPSYf$4Hmqhcup#t>g4znKxERCCg4z z&U%hqUuqp2h};%rEP5qI0iWt2>6kNSSYw_24pRVMv^b67WXIjtrK zxjJy=_<3a3TU}nwE~q}Ejg;ls`V_qaAb*|=eWN3>vcS@FJ;jR^XG|!S1f2Auu%uA` zs@Llq0GSC^gmCMr^(E?6a%EpvcY()cHfPQhffSd^-~=0%w)woSFB3ggU96B})zfr+0K zhq8EvsyhrI`UvcpKvd-4_(km#Q-feuF(=<|3DmrGO zreIX6Vu>PuVtLzs zod`SSl)(dTuddNcr%6)0uyts;i;szEMg0Wrx0U@xqhoB8tVeR|Kb6Si?X?OwSfs%h z_7{T*-?oYtkV(rdzwfp1ePAfmk%Zf8t4y{dq{zQ$QLA%|G>=V607}y{2yr7(kEU~c ziU#dA)qLxqk1gZ=e=O<$rh1fnFjRWTl;G+z_1K9nx_VY7We2q!tvhEI>)w9wC~q_B z#8MR|hI*_VekRk1i&yFa(>8TUR)*^EkmK&v2pJtc=X*IOXqZBTIPyhhzAo^3Bj&=F zM9&Dx`y1t*cmuof0dAgX@sqYs?Z`u%R?yman&eq2?-LcJ4i;fP+&`~9{pm=fLI2@L zlZhz((~UNA3XHE_$0TPL?R@j2-`wc4b7hrp|Ma5Y{lkk+kyuoT?3F5wki*liZ2`Nz ze+A^Quvn`Gq{e~d3|mmT4B|k*fmV7j4>w_{7Pq-t z#j&BXDUa^*ibCUZ#j8yq-E=VZCAL{g1c!}&Vrv=M4eA2|Ha+=Nts3RFkc!ib)j%x> zhi8YjReBn|rec@^4U$W|z=E46lKvT110*+Gsn->D81&@Q+Mv<~n z#M(P_IoWAAbUsO%nV9RYg*A#X@iO+1IpuD-pG9OrY)Nts^(@rJKC_5kjFVL%c3`+J zn>-ST-Xt>_8jGmk+CwEGnty^wZGo@Lpmh0yjEHR=Q{g=-Al!~OLSa@Q(Z@SaSnAx? ziLU;L0gL$vK5Xp|gJ0=u%hKxfbOtP#niNY%zvpZ`)+FZ4Ay-NLk>uK=5{nvcH#2zwt$XnUpqi8 zg!&>Mo{{#Ur5HIe(HQ9SsYbfZTyW)U!^B#zVo6+WVaH4^oE@kxOBDgGuE;M#Pj<4P zu9FvuM@LcF0gd_0tFGJ%E1F)TW$sXrJxG3IWK#1wO3yPPmbgk_UX#8fJf}N8M;lnv-a!M+i&bcy=FT^*XGA$|5-Ac0}@*b|%mU>%CySBg+B#*F&>ypN?~m@=K~$zg^WQn^Zb(5)RB()lh9`8sUax z5~6SriGFpB6pKls1+B0-x*J5M^a-<8xkpD?8^k3m;zYj?r<1BvYHr|)d510wAlHSd zL{=+d*N2xH__ggV2=YBB_t!*kR40!RcsF*TqOz(u4s`Kzg!qUu-#2b)rFa(;_1?SY zl*ovRLw8fp*UA;t6Lz~FMU&LZjyVnO6rI%We-(GZZ8(ZE7clYduAKydw$<47?D*W_ zQjHi%$#4)8ATU{lgEaf`f|mS4ks^3dx>*^56w04ZTLk1}h8h{8CHfqMmWt=Z_GmRr zSb2lwLDPxRoVp&8bG6CE*v7IjYTE5C)&%QtGU({>AK7&6m?;Z2aO*&F1-<*VFM5rXb;8mM1HDex+V(SK9 zrQgeNMBbYZ=%h7v5y{*ab%bx0*s4iClbG+SHYQbK3Kt~D$Aq0eN4OirHtYzC8){uS_yQWB!` z-eR)`bRFj@#x+&Y$mp?i8O#@QDaFe?$}ubW(W5)%sfj~eJ-{-!S(x1siKc2rBV>i8 zH*3)XPffeNOgJ=fIaSc2o-2~k;+Jc9x{(}@j@o9y5#@{%(TkRgdbe_wlf3%D3~r{? zD_Ua_whUEjg{B`sg6|%Bi@Ly+^VYW-HjL zaV~uf>O|6)-+*{mR@>29=W+i49vcK*!Yj!6OkeKcBVuP&t}e$fa!*qyEKCt%4_}0) zlU@PFhKuMy*HHAUi6mUz$VLK9JB5;_{2|Q5#(?AT7LtaYR51*ETCW>OI zbM$#N8Ao-suMs?70a_{_3PXn@o5U*r*vt?lGt?Fu+aYfT&pB6E8Caz#i{HlnPM9?7 z4UQV&npAh~P?qqo#lSwA>M99}qZu|7T;$XbhTpdkYz_=+` z_rvk@^6rE>Qbk`ZRrl%pmS^XRMdA6JJ_SyFZwc|FZ|OyYuK?QmIhBbr8HeQ@#``KW zKW62+2pwg8i?s5GSQFfj!*)QWcj;lV;&B#_I`j7hyvwV}b_**bS)W|S#(peQ!^7k= z`dG^OEw;S^UVb>6Z!2-k+Xc+($`A6=?vTF%+EJ~rft3*;sFL5~Le&^E7#K9TKH!6g z@H)onLHbD28-sPvkP2B&rPB4}WZmIFIJ^x}Y4jKUNPe;~?e_30(;GSGY&Wl|wj^(fKBvd5q# zN_5a$Zg(u(=h_s0_h&I~6$Vsd0Afhm)%vZ3oj1AYxIGZJq(gebIoV>EcB&y4PA!hM zYLZxAaR*t3{vh`GI_ym6+2(wO`_ptXD^s@BXt@OuHU#w-#x@dJ6QZYs>2L0b7oX|w zOf^^|Mu?1#jvvD;f`x-FflI7m>U+9nCA*ZddwIdKE$!CQC!vh+jsCj&tJ}s3F!K)l zVYqj2e~iDY5ms|d?W`HMYTr-72vn@;6!VBMf|Nz9KQKrbE&kF3QnHF|JjPAtn^vTn zp@n~@n`)(gvvLR`8eGuIHy;w-XV?{Ek!cTTanx%2*KLh~kgY6A>5 zMy?2=p2^Nkx=nzPOtt-gQ`)~&Frw9MKV{hZT^Ohw{^L`xnzPHv+NMfOgziJ#8P5Sh z5$n4)TuFLDljwI?TF)O{H%VZ5xrn8ch9_@qcnB#8D-Hl{hpKXT$KKj-Hm5MS4T^LkMQVKTFw@qE=!1z3N4l%dtn%d6a zSd=UuRg7I+za*^Z*I{$2nk05}ee3%4&x;5!!fzK54hAMhNh3KH#ui}^6h*pqwn1}( zVkD#j3&LZ7@>9UTBX}s#&%=v0txx%zcEFR-fZJDy~7o{+&xD3_CjcfSEjG}NHuZBMLF7oFzu$y8qBU7){&?tV zrE-BjX$h%NflmHBIEB5GQgOhyzg#OOS$u9ox@t)yNtTWLy6U)BdvBqjNYu(~nZ%h6tq$EjpdFfW7z`y^pZeRcL zIis&*ANKT++DtXIBuPIV<#XFJO&xDGxm9%T*aktM5boo{bm>}Jry%$FnBFK@;qBc> zP1;C_mRx3u1IWmW;$n9V=T}*K5*4Qhlih?WO+qV)k~zJB>G9zwR-;3{b179+qc)2y z(Ku`L7FJO0hKrqD1yh3tQlXQIYUqoNAtv?mWFsXcs>(;Mf)_k1F}t|girRFKbtV(5 zr(J^>XVVm)DHs>>Y5aR_$4Y%)+y}NBM6YC=i-G*;pi#$R`x)*oi0GM|S zyNtK8bjS)U0rpJ^xugEX-(=;pWADfaL&7(3DKk%6yr=<{P#q7OQpw~r9$s2IsKYj_ zIS(eVJ@-0YN6ymIvM4N%iae}i<5^)q#i}k+U@?d|HEo1gL8VW(lDVQ=PSUB z>Bc*aP*rDJiDcQK6{~i!!%bW$eDUgTGX$I}nUI6wipVKoGj|o{?O?vo^Nl8X=utJ= zO_*jdNVynv77&Ub>g_*1j8|Vyy6>5G`b&XqrzdHi3Ngt=t_5hjr3Z0=<>ka}I6E#q zPd-hE8RB-eh=(1s;oSd?0wcObNM~SG(n>q40#2y!t*?x@oh0yR@o_-h8Z&nAfV{7Y z6~C(WbO%px!y>gW-SOICu?}DC-t`r=B`khjG{k+%oS%GAXs_&=@#KHt87sW!EQ--7 z%v#**2^xrG@y~Mn2wy7QD(b%(qk7@g-}TXF`@A}o0jRxLuw}q+R9yOXn8G=?wE|l7 z_Lo$pFlZHN&LkKd1k?julyg;G({(~=#sm|$Ju6L7rzy$dxjm8lqxDN1;U+Cx{*rvw z8Ml%fu_IjWeFao`bYMO!D);G?qy~;)`VZY|yd-witux?!sTff(RV`hKvM0|8jBKfb z(L#>~gl-DXK=y@QRFx=fo=zEwVn)U1s6K>0D;STQRLk1Q7p1 zwUyWm6jxu(?&C@D|K%^lmI77)IN=Q0i$u^{*eMU$_tztW%II3YuOMR+{(iq`2#yQ5 zX6AJFMqzzBhW|pF4Bp-nJn*?$n^Kdm7+W7GNJE~drvtIdWTyGu!KX=B6>cYc4)~wP z7$jlLt%l+Y)Ub^Oyre^a-qRJRX-<$vT3+-L1OqWF6WB{}U7{t0_BEpTTv(objgx47 zvT56LGW{X13q#_Z$~U+R*K%>$vxk`dG;`b+%-tQVo~`seN}GBGHn$5jZsKre8=oTt z4Ja> z%X>mqmG9LdLoW&51M^WpAF`#Em~m~6HDGsuK-9)m7-J$Dv+Qchm3lj`EZ9=(?I%#vr2|fiU!mu?ElaI%l+p9!JrZ z?WB1ev`OljRrY(}@FtVAX?eH@&hGPMSg0xMFkWrmvz8KOZFNn_cE9P=`Kw zY^PF=F?wN6|8NlDe9<|!!RzQqOI(#dV4rZ)RZfwwOYG|L!;?5%tB0~g?i$~P0W(uC7-sTJm$?J7p)HPN#0ibm)UH<{0QEZNki!spu{@mC+2wElXT(auZ*$MKn9L#OLwq$ z;Evjvyod%b6Lpv?vvFpB{ke!dX3~lcE=oY*N6J}V_8FGShzo5~IqBU$Fe?^bzB9S> zom)(+t=f!9UV8-?-1QT%!9N>(?%&mFi`mhW+~=7lO(<+$?m)Eqf*I_;*Qf(z5=hKl z!nxg$IjD)Ztp5h9V#l>Lzc^6r>hj${6Sns{5_%pdm}IhX?|93AJnzREq1IMbbR=h1U6 z3U3+o*nWzWY~*HS%&H`_7+;0AtJ2y~Rmgu=Q}xULWHF>H+elva;tEj*C_=0s=umXW zB&k@l*bW@w6FDx~f4kO#80?i4kh6eq`FNj3+oLF1O{N%ia2{s%v$3?0;Nw|h>inXL zT;FfrwW2G7vtzRyU<5*{e_%-ayfuj&-CF)DAlkYl##&E&E5~kSv>WpKLbN>B2Urj{ zc)6|xX`}Fj^`Z4Nz}AIsWZdn%BL~^dds~%#>nw49>j9SAuq>iS8M&~W7qAtC->^< zkDi08A1FT1@@Qwc&$&yJ&^NLkeiiSWlOUEVU{mC6b4F?rcKcqSGM&6)+SR|F{l2P=)s+jXWXtX`Jpc6geFo)fda4ZZ>@iHPmC|E&MLqHWX}3-Yy( zVW}P;_(%=embI28b;cpkD@BnPPhv^CGTK{-q-l+j6T(!Of(ENieQZ~zq?NUMa(l$6 zm;iCJ-9Oe=H#4#AC7Lav{Kp~E9ojU64(c{P<;}&KP1;09BJE>uAAho{vNtm__h!pf zF9~SPaG{_^XE4}cwH2E7mr|dv(1XHHqZ|q5i8KI2Oqr``3mANd*^xC^m#eIQ3`#n%dgp@9SKBJG$1vF7!a)5>B0Om`LfsDo@tt*z;U?|3iH>;t= z`w~luJc2%t#3^^*6kB}P!2n6&R39^1=66T!5Zl+PHPt1DMeQNYom@FJUsRYJ;~qtN z`c!=xaj=j5UC7Zm-5At7dDL6FzZv$Mz^_SWifF9d>6x=f@ms4*=g0jX*U7HhUi=U} zlT33?3LxCJxRHbBAM=PwgzL9U6yh3|mZn-1*1lAV3r{;;rkmsoyO2-#O3=w(pP!-X zYL2GsNd}FYHfzd6LyGSJNY}&x*p_%Pbk459>j}3zs}G9T?EUh=a~#o_2D70@+G$`{ zk0*?<`upPtZYlcw?9IDRG2LjBwvNf{{R%|Kl@bUaCLFs`!1xK-uK=inn-0M?t;`9t z;qQq$NB*@cneS_f*{!$ZqOW>CR8 zKe@Ww$>&mhfU<+LWA1qOwE@xFEQ6f6pNQutp`v}IBCAc2@Tp;XZE4X#f}q;P`XHO= zt)79${yxkAaQRWM$}$}Txd8~;!5@RQSRY2!9=RqdGi{65U(riIMe@=&z>1o!N+zJe zztLQ(aIm6R%P*19_}vY$jXE0=EfYMM2u+w8|t-sM$&}UV_@#eZ88hWwVW}9m}9!(a-ls z{NK}K&9t%<#aU0Bx#6KC9_>I{PHNIxs3R`GAf_rq=&FujB|G)4+jVxvzAB!)FDgsh z0+eWW%ipbZrQ_fBW`I)R!{R!IcwcS@ZdS*A6`$5}EvVTUTT34-GP%c0gC)sn;FQQw zR;U*f%i!)~%`%jxgo((?X7j?%R-;b3rTNOWvfezn78MTFN?Vq$L{M5L z0_$;{{0c@Rt&xjKOT#-ANGv$%Z8D@e&KDI4mHcMdi_np>nb)(V zg_7C;85SEvSI>woD89|W?>t*gMarn8I6p(mj9@w$9u`jz#v}`EmO>(y1p3a*5v?eqp=LVd7JNj)&;z#bj>Zm` zr1EI?&3Kq77J!HhjTtY&T>1oo31HEP;wD;3bl#&6fwGC?ocL2h8H_DaCW~X6(F#KR z^kCa%asq8>>C$M|RZZMs8}MbyQ@9qMs(NPLtJ7Wy;M7tS!bEj!729`nX6|y2 zd86^Tp}sN!O!wJ&!NtHv!PZ4%#6&Q)7G)_PZ7nHB!czn(0oMhg1=bXMJ*S?4rBSv= zm8;Tl^zKEW>`rq~cn2lCB$ke)pSE3XDjvhMdHt!hk{$^Pp;^MamWImYn#F4b;mplP zY1U+KmFL>)jMSH+w%+H9MbycUUuX?Q2$d>-V!1;^Kj7j7#q#af(Qa*KSwJ1rP-VOV z?vUA-)8GEmG8oyh)-la<5O9Cz-@upmqSPKA{ar4LTm_!~UG_qO3g{u0@0%7TkGAq* z03Dp6U(*n}+bp2~<%2>pq3!T#zbe`QGc;i=pGq`Joe9$m;VZy&F7LimXiUyxy8PRy z0wLO`rEgNY^&Q`hiwb|?Ip60cz;5F&N%)OM_htDK&inAk@ktZ4H_*N~#2r)iGRb^H z-(iuDzYI2OoZ-nba5rM=s1YD+`*fdUMm%pA9in;Ks*|H1 zjUhhOB@^VSo}l1e(phSG2TxtLj!(9R?Xx%?{R-GQh^yrpY@rWW91MQ(Cjs9b#ND~B z%B2mxIFguA(|4@TGf<%K9kus}E>;z&1zxVn3SznGj`ni8wZyA>D{FTbycZ=@(BOBh zzlL-XIz(aF>N9$x52(>KtB?N~+R7fRsydCzneC(IFapDVZ_H(a&>O%}!)UQIS7epM zlmA6K33eD;(NDW7asfXH1zPt5VWd}@32906T;!hW;-EX+%4T$I?DG(x7j@$)|Jw=9 zWn|=8Pz{6ivT0(foFO!cJapgj+yL$AQiVW||1Fl2nyf-nxB9j7S%NBmvfYA&F zD<#;9pMa&1_$K^$WkhwS4!!*BhAx@tky??mJIKOuLTyZZ4NEnBfL zHBmGUFax;zZxT%CuZkZn4pn@Qavf$4qVDh88yNPDa8e3r6L|WwzkTmh%a|%0CvNryyJ$K;qzdLz&KyvP0zpBAtJKNjUVO76G z6EMjZ%|jTT!`bd5b?=|kw{e9F+n`9gM9F2zbY}}eDXCqi#tM;t`o6@d1S9C`n={n)?0=A zv0ni_JRy572(E>pXmj_z6fH+YC$aunoJM*u;PTOeOr3+ifJ-;Y`7G0AEaBs=t^i?_ zX_5D(TfZUA!WrzNUs3PHq%#t zVy~c`DKo|AA3{b*P3t<5i@Z}Cg%>R^V^ulS+EQplQtsH$^$<2DjK6|AQ>E&Ym7QtB zm;EYiP47pnz_gqbQ;mt>kz9XzXWIi6Af#;fgqjs&qah@N)!V?KE!S?vE|bw*5Q0rf z9~5lZXznODH+&h6+B8g5P}WH4yfAt|u^L&PE4a26spaV{GYgb~2e4IFHyo$2aao8n zwZW+bn;fX|_}+dfwk)Y&2&OMNB5UG@l|WQes}`+^{a@|9WmH_v@-I9yFfjPw4uiV} zcXxMp4+JM@7~I`mgC;n^g1aO*0fIxYBm@W=2)Xl|bI4*Z1vVnOfHvVgR~R^cA)f39Zo@8@FHN^yosvbDj`)-4;cj!3 z?K~3pBp=HyZbbaE0+C~R1V8)?LF|RK6L@N7sw@3VjgYnL(yWZO-8EQR{2c>xPZH9e z!O>PmyF>!#4`qdFxhR!tcdi&3sLi{V?+f!r)ilkY&CJ&^WOns7W5P~xX-g$y-!U|l z*Jm)NmuBY^NYk4nUlM(Fe?eD2!+P*x9gj?i6xq{-N@uh2`_Q#Cwyl_Jdv9)=HL;XP zyK}z)TT89I6J0|}gtMVD)dr&*ilew*9QpX52s6d2L_>QWmAP?q+_=}~m6lM4bBBzj zNlTz_9wGHJ2UNeVCX%Cge^9LB zNo1>?r>S%y)dZ=~ryXeS+bYyPnESZQGyiq*uW9=BSe(slU)z|S(xksE=Ti0S`|Nxi z?|NV@>w@z7D?BM+5144}c;}EdfOlr_8&f_vsolHj7IL?r+qfU{9K0Po$J|%Tgb3YL zX2w<@>nZK>d`Aplhv6%F0p)vT3D=%a*r*&+p=7oTQHlisZM_yZ!M>T5`{56ACK80j9DuUl1hI! zGze}8{9v+v`EB$GuL0wwraJcUV#4e)@`B>43z)vZ3Fo$gC=N^YPFB)kUUDtH*tgaE z_;=vi*EQzPWFBIN`z#BVw8l2S@xpSsq`N6}SC$Wb3BRj+W!DBn8(s?%uaDzkPPB}& zW~B=dV`85xkOg)j-(m)aUJy~?)6ZePLNDVBwL1`54bJmtfo=G%C79)qiEu9-hn2S!^ETS=4gk{6FW zuV)>@Z~|LoT1;0+xmz4aU&{wS?*nT?US#+QMW<&IhUXW7WYJyGD?A4HFZlS=lDH3{ z-%_z{Cx)x3AEPaBCck-xzUc44^t#J8=~Bq6KipSRA!-FC20`B#x$I@N(j%41>WF{NL3xIJk1~Yvk-Cd9@hf`2{!_J3(?F1gJ)cHD9Xzo>WVJ&R%_j! z#Y&|G5|0)rZ7uPOF}AW3a4MBQfV6%2Egk-qbxqR&k1d8ArvAJFw~i&}u?n)`8s!~w z5)DB2!WWFTCGW|fBvFOq6V}(&sdO4OK}mI{OD1)e64nYodvMP+JX7BKby$irG0Mu- z)}~WG_bD+}Ou%^iF<7praHeSNhbRHFYqMW3;74Y^r}@;YAu1$V2`qLab4(vQ=mZ><$_v+kKt>Gzg&Xh#q(an)yxq z765=Z5i&9fo%36qY2X`bqN3_{-rw<_;WtJZZ{nBW{sDyT(vNJrBb|@E$i@P9yo~hz zrWN!3IxWhzktF2OD_sp*_ z%SWbppRwfF)oR|Q9!C{Ux7cB{5tnO~qAy5G6^vYYV>~~ z-0VHY63dsE(_V`)5#4Gfh^kb&(VL)JW>%tzU)i%LIwqQkN}G85u7OaXL*)LW)4FOX zwMABEY$4$B9|W0TqD;)0q4j}a^{QQ{_$?0jJ=Yuy%H}!BFu?y+lPq&?iE9rR0V+u) z=9QCN3lk3EEK-_;5I}y@uU4Cmyx%M)-Fn^-1?^kaMyh>QVI4 zUT!=ee@!V-)$;3W4sItbrM};mwElY@ITLYNG-+?YF;raKUbAf%9{f%UmCp+sxGcG( z0SqzF)9o<$i5ArnY~4s#m5#<;m=>YCWv}+g1(eiY=0|8RmCv&i$449-8KMc`e)mQH zgk9@k@#<=~=Bh&fvMlXVeki8dx~n0Ns(f`!;kD!xo?v8~Wb=!@vfRxnd{u(F_p@I( zppJVM31r`Nic7GKA?gGYp?PfjGLDO5;xjt*kJWzqnZuY(!@1`R>#R9x?bJg^kuN0E z?a}B8HFXy+>x8lD*5q5Gx6|?a!@w~H4|jBFiZc-C4+1)AR1nUX?F1?zVG{I8f~w4^ zL07ix=vOFn^mie<6t!Q;g0qI|2MeXiMdFnu6H+@F`ZdM8oDG(rYRzI_56&_yy1W7~ z;z}kGX7bez5GWaKOjjq=FD^AIc7%O!p;VSeqwriWy&KwFrLF!t@v@<;HP@n$(w^Bn z@s&cAG2phwO%nX`31QKRyjSKU_N|{rct376bp*apKBb7FFL%yval{gRAN-S9*yelh z=>iJPWm-^{y>M<7M2_P_vBx)emhdSHsrbLJmr&?#+xF*MLp? zTfD%ypPC=Sj7=Iet21g&_a&(UeCNo5s)?F4_wv^+d3)|&I33jLd!hD_<)4(n?t>P~ z-Uq|Atqc(H-!Bq$I;e*gI8V3BersWAM~c&#VaMGv#fWM}6oZM=@<40_q?*8HtYGS<&m&w=>Jo`rUgQqSkx z$8?26u7Lfy}%z{&b|p_v?(ZOuTBZzv>Nw${ou6=ULj0(>S%mf;7O+A_&xd6>p}ww z^Mc;@iv_&i5j+0(NLgwqr7C{RA!HlU#K6}dP6{JkCq_>$@HUSQ>M-+3l z?x983Lrzo!0jqvIWFpJ4@H11vfH&wLCPfhF9fVl4*<)Y#P=U7P;s%}fCbD6dKfZka z%}U)er$fVyv;@Q%v4D;nm3LM`V}mPF=G>2lwx`V>3Ju|dn>OTqWm^Df8rd$hpqBKcp=2E zuRm?a;qWHbE(oo>3vU)*(`G$3KA>R}o`rEKdY0B2C7#Oto1%%yx*3 zZZ}GmT`G;Kh$r=%67$~c4in1XFUU?T=;SM1e%o>Sfue(4M;a((cW%`g(r3Ln%x@a| z!(3FP>4?9b`JSR)OvXEGZqqt%7pWUWIk~R&Yc@$Ic0h zb43!2*+k3lnDfyZ->lvDhTLo?cpSMn8NY89(H}X?6}2y0|6I&;yV*6@FhVJ7qSlk1 z(3x99PJpJa6g-dAOi)?{#nn!xJK?G(q)dDTd>Ld_gu5KCXQm`P5|(*0%KtkNW3xkr zQB78RSB241$Cxoib8yyZPaiT8YI=uAD%wiAS$Qi%cE;b8Gwn zW}v*-9R;3(4{@>yAY?$*ANw#;Yp>aP6+pnf<2bauxlW9Uob3$I|3V7K+OhL0U2n-I zh>*c*Q&cL=08I>i8L0O^3jx&y3}K%|EN&txDj$m#lEu)mffaojkC3Yz+$x_#$Z=W7 zm!2s@F(y|DEfnu4L)z82azSmJ=8E`ZE&B0% ze*i^bQcqP$)0%lCa#a1eN{B$#TygEG%o2D#;W+#-56Xg+k~y4la8AX7e- zhf9zct>Z1-*UG%l6l+CFzHbG*S&?b4BT;*Knt%3_t`O372gwRL+bF0gkuaL3gTq*5 z;f6fKB#vKed&83=!wu+#+iE*cR0rvaW(YmOpA5Krla^x3vTPF9Y5WF=Tk~K(1T%H8 zT%9*Uf)+d&AX&Wx^RgREtml+zm?gtdFWFS-XU#w4u~)*Su)^W74<3_X-Z-+oyKRFI z#%*LI5@xlyY99?g*hm|o3teJ@Htky+RVL&}n>(b*KeKI-wxok$%J|de&c#~KMAg?a z1q8?`5ck$E+ocC1aJ~cAUOONtvr0*}eF_)G$U`efGhj5MvzjX_{1hH1!$8AE7sq4R znCTO;?G(1p(&I2AvXI`|YJ{>0mK(`J19I%snwbZ4aWDI$WQGLEj=uw2wYG<@7H51RvXS?=tM#-f)jmR4by-t-AVbfj}ypQkc z%IJ`2T3Vrv%?nSo0;lZ83SSq9NsNccco&}zoXz_lv}o`%+eSH?ZEIHe**ah;&4oWIfUUkv^@|c3YI0Cd&$%^BMu5@=9=7R6zSe#B zE!|72QuTfviBR<;mntwzs056Ul{;%x2Z<-n|9^xq3ks{MvtMYv2{z; z8hd*&l3{mVp_{SIK52Y`V!wa8NphVYq!QZ(O$GKiHLVKVI-rx(wW%}O+E_;~{z zm%@izjyi5wT@F+{Mc$7Z_j}9CPSE01uo%uL)z-YjXT?tA{9cG=)9Fc?;ahBlQZHGM z$}9jr$BMRc@(1wDlT(mI1#jvw+j`7`AIg{FEf2`WiK^ zU*Wk%8!+KCv<-4g9$qO&UM*bi0 z+JL{n9H9SK1E&4I7ytjy41kWU0K7bZKNc@Q4iF6ZC*tORwjcnYT6q8n{*(QWdN2TB zHSibs{ofJJ4Kyqs{&x}p1qu?tQ;Nds?}R{XDUbaiMW?@$0dj%dAPDWw-^#9ukbwvJ zc2a*M6oHw#0G_)|O#HVDD1@~09`Lf<3NY~B!T}&fE&vb=|2H`Q|7?c&p9}y%>R&8S z>Ay<H545aRfEnnP3}5IEvo`R@Wm?fIHl0C3=MdI1JNTF3-Y;12L_#(WRp10o55 z;fPWHleoXYfl{P!OaRap@*gr3D1yPDz`ujIkp0cHA=m?mWmj?XR7x4e+|N0rA{>y@Zi?ASo5IINw z%Ypzym=MaKTFry!7u@r^Qvb9d{DlLBuq#f}?P9YI=yFX$M!qW2M-M%yvjd*bv~MJEkv13P*kNK$R!Q zREPC6m58TbeI(?+-m~KcizM4RG@asJ3))wa_q}QCc_oM9-o~Of+~mZIbDjN&XJEmJ zbZnpDr6O?rOtU0fxX z;>5jVb+2{XX5@WkgSN(3E;2SNv0|-*z-Ki^m+m_n@0}v8*|b3c96KS09OK_5D&WMm z2LY>F5uSd4hu2_6+tz|EB`hS!8s)6q)TeIz@iH&|Q_mByRrfrAQF(XZ^yAXb>lK`o zn_@X5gw5zgqV-v^7wXy%X8yOUDYBp}`2G9*cax`v z{mOM_T&m~NgW{wD+{B#WpM9^d5-fW2f{T9&B#K6;`K3~3aRuCrz6si<=v*_ViZ;G4jJ7^VnPk@4 zUVTiaMPRv}YW4wxq)sIpXgqIlsrB1tZ9?|lZxmQcXLmj@w-B>i*<~B^Y|OKKpq&3X z5_ta#bsL!1*4zIHN-^=Mxd{a1>pR?a;Rp+`5y%q0u)b05?=iv8o-F&d=f|az-qjC; zyP7VN(w?dFdx5-^g{1LDm!1Yr2GD2?&+k0ohM-b4B4Lgls_3@y zsw3-@&oBC`UI=hz5vX+Nf7r>@;w+r8@W$88LCW!Ub3cjuUL+bIkBB@&9>p~U_|APh zs#pvoUmq+Vbx^kkuRqpH{#0z zO!St-d|;iT7`TbILgk35>j*$KdCom7T{`jTUm}3^%a>v-iA_%R&i!NR9hzTUIfDo@ zwo!4D>td-~ef)VTI=xzY!aD&G%s`<6k%#!MR?&nj@6e_2yxE-oPLyfTb)xP|A)n!= z3U>%yxmo#;d8x!!)n4$A2TBdCGsTF{a}?T_PX;uxro75L^j$)~)Q}lEfge86iAX^c z8RH%k4TWzd-Q9uM1k;X`D6=}$2hU>d8Li^%Ow~d_=hh5aW9ybhwkvEO9Pg${rG(!Q zhVNp_h!3Ja()SEL^`9!4ow=i`ev`HI94t!KfNi+T*fGgX3aFM`Ve`(BKiM2uH00)( zboMI@p6T2bdE6DYd)qIv;*NwK0$g%Ir(FcvHs<+)-aZ^JH?&|VY*v-lY|jrR^m*V! ziZ^13t+2nm0=cqWbfQ0g8X`~u% z+G$XD9H`nVJC%cI+6s|>q!+d?@SrB?@g2*w9W1VM?^EAm+ME3xiqiD-gWx?&X9>SV ztEwWC=Cb>KsXt?ruKmGA%?uv_m3!7=;vvgRByV#bDmGMs02k536fSqgyzz2PiX=m2 zhjpQs@k!n~+nN%&7G)3xRqTZed02A&$zCFoXb4T!cbt!z-GZ{;bt4S~x?>`sQKYD?B z6Z|Pn`4@jQm1Y+k^@;4Q<(tO$I}dLvK3uU`zMz(AEU*&GSA-=#z1w1i!0n)VmI&wy z&u+8e5z(-lb98#=F!*MD_xI$VBX|Xpob*6`?RUvA4f%t3Us5yhbR0TVkJQ|uk4TEXq75Dzn3teXt69Cakeu&Y64z34(KfWa ztgwPlT<1)j3-wpL{GbLQA)$WJ_z9N=2w=tnImeyu?>ryK(_j7C4GE*Z!*{H`X~P6$ zc_0y1YU~Py()75p5Oi4X^Tp;eq(?(2K8I-Pyey5o!t_@@j4n2#?%t}O6+oR<OJ%aNIX@{dl=e*gHFE5g8bTZX0|23CS3Dv2?XHnv zJBA;9=WYpyKvnZUnf(4P*;{?v_HPUyv?{x*(G!Sx7B^=Y@clxjayqX~Rb8!ogMv## zIjeLJ!;?!JWWx8#t&yh0drP2@M9^On)H6p)j_<0*tI4Ag`*L!XNxRHn3T9Nab4BJqP6Uy2!R*EGpS}Ek* zaLD{duloEP+RpoVHWVbAtzXCVO6vK!I+&Y&V&Oh^(E40MX1#U&& zrh5GVTnnUAV25>oQt^#;sozT$?-a=`%m?hf#hd2~K_Zusq3R18m4$1TNoDWlaPyEH z_3kLNSyoJ0`~iF+%4*;e46axKa^E#Z-sbTR93lXT;OW?g?Q;_ytoruX!Zdpuu4H!m z$6~UnS80buw&Rub?4)DA7*>r6F;wTqYfQ^$dWACm?rw8`#!bF*`Ci(vx865eG=>VA zRjR$x$G;da&kf@uSLo-tkOvS0!S8OXsdg#Crk)5AxXQH_t|4@_Ze|2#qZ?!qB83r0 zThd!X?)Xj4Ba1Tux{8(Bsw9%IyH%}rA?o}Pn|K|`W6k_!;ykMJc7}u{Gto{gZf#q? zo$;WFN+3J{wVjV&B1*slM}Oa%qDQ#o3qN096fcG88~@6~<^c}&&O4g=xPqvjm|>hA zlgI*3r+YJ(HK}M)+q%8eU*^z_5p73E@^e;GhlFbF1j3MYq@2+C2Zu`@okAdhDey3 zZsthS*$6U#NQyo!q87H`MFMIGqwrw=mxVte0#QtEfycy1jJq@+?49}{kK^aW*{IVP}#l^UroupBX z&Nr0C^VE6^Q0V}Rg$qi!@p`)TY$$==4nEha!q99VB0mQc>>sTEN4Co(XjWy-P~)oz@(5ed}ym zQP^**$uZ+VjGV9DV+GUa*_F#Uoov3m+2r&i7)>;d>NCEy#tmxVViE4?C`&ITI825k zukwW-%6fhI(9eC|!$vdP-96LqJBDaprk-;~;R_qn_v(bASshuw#i8!?E`HXW9>x5u zTECVR9u<1iS@(#)iWm)#+!6ZsqP?W7#V|avVJlJy~I3s~P1#}xm&uCEjC?#-*S zOP~d^Sbi)2$Q418mi@)&l}b?8yY^?3B5rWixXI_sL4hTLAsqb+S15HxYc?FlypDuDJUj&6LLn? zN~SOLbAJigYF6JTY5utNj#O`n4*WxOcD0sy^11VZZ>W((&fG$b_tj1*x$}cC#nbY`xZ_87K|iGm@kbToFFXdD{PY!5jD(#nc!s=YkEm3lDVpP6)aS;j-wOhnUv@|>A1 zA|Dj=sXr1g=`57O5x&$eetHeBnsSrQj;0X{6= zGsqGp&l8WgS$*cYuobW8XBd5(>#Udv9+2{Q;>*`kQ2)G7R81xo8|e{J`KGwZl)sp||{C^7N!)>{w&Ey%(a*#`<8?i_St5SC~zcZ|NHv zS`((q8D3e;Aq!zXHziu9<^Gv*U~6^n>N_3WHuV=LT2^G%hxJn|1st`e=c18%g}0>Y7?Rko>9FWJrUnJTDZ%{u5Es?F4FxJs?YJ;KrS5-VHh|fb^QxP9MdNeqv(Cv%+fET0D|Uj zj~DC)NkQ~Ks>(ig(CFqDMfho%S~nz#|JDc*Dm^38WZQR6MxN7VY@3GPD5oAKbuYRS|dzbmuJx>E7jok+9S(r6}l3kD)v)S#raWBJC?3FH?B z!GYN6u@g{&NU(}m2rBHVFf+Ks7#i7~%uLO+o3?DmDC6E&2s~f>ESo3{wTSksWX7htCn$ zrVcmvCjrr4YC+5;AU^BoWkjaO2RRR-M2pDMGeqF?{+CS?kqrkdmn2QpH?%=CA{x!$ zF)J2=xDi_sMh`X41MZi@Ka~Z<-s^@W;3G3oc6h;d81+{r>0MFkW(QtLG{4#TKo_Ri zH9wt@Ahp9PudY5o<$2d8`5TA=8&?SH&-w0AWI(PACgYu;$tMx^X^w3>9oZdna_01J zZDUo%>hw(S(gqx7IyT_>ZkJnLKCon?{DSxI6A#rLXC=>b=XFKP(*S@{(ymzhVR5U8 zXzA$eQ0TYc#gYVy$Xk9aEObG;luMt`Vpex+y#(mUR1|r|MUY93lNZGuwaz~+=aMFF z@QlPSzE~CH=MgQ(P?n-fN5v;M(#%cthso$Cgx;aq&`hQ*&BfzVO4J?+m!^~N$)bX< zwJMA5?L>)l#$X6uFPs6IB>Jf`oh6Lujj^u`%n|I9i*FSf_AQ&#xV4 z!+>&m%JkNK@*dpPcoOVwnsDbmD0}|stzZ3F=5izYx^^wZ<2&GOK=ZzIoC=Bpb;@H? zYM?7wnm9iGJ8BHAP2BGu#+59vd!Db|LrN+Tc=94eh{E$dmDmnrq?Z15_vnt6WuCSz zoEpkl^VFudZ%KX*da$oRgr7dqrNn9(W>B9PDGk*U(pFqun=RxaX=1j9e)c8!N-skh zp1iH;*tBnBnaNWnnV!=GFt2g0te#`l#}#X01AR*Lc2P?P9wWTwXvzH=6d&Qg#T3$c zEA{WZ3}v&S2W068n7PENKX(l_ z>oe&YGnc<2pORRU;nGm__5QXUTsm%eZk+6eM7d6mEUu->$SwV1!lVOtf+Bau19|3d zh@ljRKaWwI9-Y86N{sBO0=caEkgveO$7)0-!EcIa_f(;8-~<_+pk*(za~IU-)$!-G zB{i8xVJZ560M9t8)E+=g6r+-)t&W=r);4zW5RHtnBoj4KN)oM;7jk{;V__^fMp~{B z=)ZS1u2BxV3PN4;Z>s`#pAlgUkVj@jqAA4z&ECEoerUluTbB&2f!xYQGYgxea@c(i z2Fb_ufh`GtL}s5;W2P-Ax@NV&#^-d84oAS5>f%|+E4#gY^(`3~M(>CBT;*1aL*(ZA zp83DBc;J1syQwQghm8HgmJ=Tt{ctqS@q<2Lt77E#b{3y)3we}}ee|&@qk0QZQZr^Z z$LkM(X{$NOH}6g6xrNc>7r=_#0Obtiq)-|PjcgWSq*;N!sOSGF^v*mN_22_Nn;lN- zV`5d1&UR56TYv{z;;=Pl$I=ktcV2VaCEPDtB> zMvH^D@XvmWw?ai?IedtF%Mn?Bgz;iL_Kq*6IK5A_6_Z{-z-VU1o&wW%bz@(-vK z6g*f|ZL|HzG)aphG%sU+oKeiN^xpk2O!4}fEH`MmF26eXd`%_mY^2e$M%q@WJF=jq z*7YR6+TnNWA3zp2?4oZv9F~||;^<})Z*#|;CF9E?V+Ph)GK;2l=1v9O5*%S_@pB;4 zfC|uVaR(~pky(~gfM<${Q4~zWwQJWzC(Ub7qi;Et2qe|(wZBW_E^6RAlIxurqls`i zwj8HqfRm4HKJQvBQ2-j%{1yh(kZH7OhBLkZ$=0L9^^N+yx|cYo;B^<{O@yyC!f>=D zQH5$UvRI5Pk)QAwqA-YJttq3i(m>f$)|7wN5P(6Zn! zq;|lYiG$ROnT(`WDDCtD%mGJi*dXQ3m>t2S z-=pVWF?3ono3vkljetH~b|X#8?8D2*q%67|0z~Q7(Nde5&E9(=9D(-@ZwXL;o0__x zBGJ+}TY5Fv_HlaAl5A_O%=UBOGTqW70~H8~HU}T~>2(t!TDW)_geod3w;^r#_~p;Q z65#WpdR`%|o#gMys5Oit=yHB^Nu+D+zp`4lu?dG0Js?O+9(_PF&LWd;Rb$+Gj3Cc- zw~l6ueSx&JWo}9&QFPpe8{(glqPUOzeG?NbshAEgUm>w2mD1J&@FjzOTs2iF#7vO^ zMt0Jzops$uOue2{Yq8Y$)w^F5IJB^iL~2x|J@-WK#=m6;Gt*H^a|cjyN1#YIrs=Zf z2M!}Ksv>|9E*&iJafgZX_V%ok6D5#$$zCG~v^3`2&Ed+0r;R0QwfZtfA71iI=@+t4 z4|#lA&~v64#iG>m!S0`nkh+_CI=1~D?<%2r47efMRoV=hJNwOGYvl)WN|&*1c-446 zyZi?r{Hy;+((v)<6ro4`w)|OA|Kxog`jMVh+T{=6`Xhz}IlHu5)3I=fE$vxMKCc5A`?qasDM-WTh6Ogw~Zg zWe!Qi`*Bh?flgM9m)sLbrr>u#q6VQdlRK26!MCy7iK}Ce3zN18QESs|AyoWK7C0;m#)$oP)AoJ*+R0Yhkq9adBuh z_BHPn115B=2xx)BbPF_eWFO95L=pQ;o#8gix+S$U|rm5gWu9IXeW7Ob70NNbj3Czk2a(&N6Ri3v*s2Qtc0!`PKRO2dD zj+IW+p>^obJj!|&$yb_7`i378UmUvXs{s|bdBv6VMM?N`xFC?#;NH3Vi;|eb=)?f5jqDAA9FFZ>5i~@@0#?_AW*|!)b86QW!=wc2CmrE5pGg zV?}}_{_A(F;R$**Qm6~K3|i7V#my{rVK~UkCnD}kIi;BLD}1q}>F6AoQ+&+Eh1&PL zL@2-myVz-D9XU06HEwJIx@G*bwL}Ys1vRyF`km0pE}JEK?%cpcI|EB-5BEFxo#J|1 zgFCFgRB2zGRo**M`W>=OGCPP*>Al07SYzk_W-;jEXIsdjiQ!Sz){a2; z$+kbxSyCmgN*S%9`3hF3R4&~Xk6k9fTS}*5-5yi61czJ3?F+* z8?0-y@~y-d*hdh)yHfL=5GUb&CRaE8k?{sRjp-99uqteVn@()XW%b^2NM9+pjRp;m z9rfK~ESOD>&4o8jpqil5Pi9VPqG?E9WWBD5S<7ne9VcIQY4Dd_$Dn&AZZwd^lP5G>FTe99ng2V4d_KI$`nIQJN!wMI@So7{wiVrdY;T}Y_n z>5M+)8xFk>fBXY@q_nVw31RERE|%H*dnZKNJ^t*!N=K%F0bJ%TSJU79{4IiyM#~4s zJFHHo;MEaD2LOiV`(*$sKqvmB-wf;deU?tYO3WbnWrR|%P>{kB3#r!SYIZxe69!42 zS(75Hgd#dr)W%_0p*n)x%u*>NxXDQ5eah0+H6;6!i)udKaAsd4A)`eIDC>kTRQQ{# zS$Ui(qTuO%DQ%L_pSb?yX%EFT_9BTajXnWHVAH*6OU}wGYX#sds>G7djCq-}w(3lbuOgj9TK4*wL?*W^pO>@Wo-}wx2c=5Yv10AC^XqHdECpCDJJ|0wRei^LC6VKZRHvCknK}y;VvSNP(owv`2CsOAeGlU#|)~`>U$r6Qs{uEcC?I7?-wd#q}-%1 zdmK!Y_t2+St)P_p5T6_`I97EdF-552;<_HKm3-NhCml(%7n#x1BM{vhG)ZgZZ63TA{f&^EIKI_Uz%aKY#TPdGrd$g6t0y)az zP^IVOu%JRqN-mHNBlfsG8ZYV;qjNvKcY;YanTi}NSuOwi$Eg({Tq#hbk#q-u$sI>s3S*yJ=CuO%Jn#l%4l+})6ChU&$GZOvQh>@yNddyK zT@Co9)X3rn+qRfiRtR(t*PK0xYi*+HkmVxB_~fzm`r_q0k&$0_;(m-+csG(Ml2Xp? zS1N?UDxphLLB&Ig!YJyg+XbC-N5gC+0hY?8i4u;5ssy&_T99hpw4wX~_@u34?S?Db z!aI?s-(3nfqUcAc(9s?-p}8?iTet?|tTbsou9Yh`voKagM66{5noi0TQ=pE|LJ)3y MKM=X?O8%_6A@Nx6&<0r=&Vr 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 JyI#8$|Jexre3SqH diff --git a/tests/TestBootstraper.php b/tests/TestBootstraper.php deleted file mode 100644 index 1e390c0..0000000 --- a/tests/TestBootstraper.php +++ /dev/null @@ -1,46 +0,0 @@ -setLoadEnvFile(true) - ->setForceInstallPlugins(true) - ->addActivePlugins('FroshPlatformThumbnailProcessor') - ->bootstrap() - ->getClassLoader(); -} - -$paths = [ - '../../../../src/Core/TestBootstrapper.php', - '../vendor/shopware/core/TestBootstrapper.php', - '../../../../vendor/shopware/core/TestBootstrapper.php', -]; - -foreach ($paths as $path) { - $path = realpath(__DIR__ . '/' . $path); - - if (!\is_string($path)) { - continue; - } - - if (!\is_file($path)) { - continue; - } - - require $path; - - return (new TestBootstrapper()) - ->setPlatformEmbedded(false) - ->setLoadEnvFile(true) - ->setForceInstallPlugins(true) - ->addActivePlugins('FroshPlatformThumbnailProcessor') - ->bootstrap() - ->getClassLoader(); -} diff --git a/tests/integration/MediaUrlTest.php b/tests/integration/MediaUrlTest.php deleted file mode 100644 index 50e2cc8..0000000 --- a/tests/integration/MediaUrlTest.php +++ /dev/null @@ -1,176 +0,0 @@ -getContainer(); - - $this->urlGenerator = $container->get(UrlGeneratorInterface::class); - \assert($this->urlGenerator instanceof UrlGeneratorInterface); - - $this->mediaRepository = $container->get('media.repository'); - \assert($this->mediaRepository instanceof EntityRepository); - - $this->generateThumbnailsCommand = $container->get(GenerateThumbnailsCommand::class); - \assert($this->generateThumbnailsCommand instanceof GenerateThumbnailsCommand); - - $this->systemConfigService = $container->get(SystemConfigService::class); - \assert($this->systemConfigService instanceof SystemConfigService); - - $this->context = Context::createDefaultContext(); - } - - public function testMediaUrlWithInactiveConfig(): void - { - $fixture = $this->mediaFixtures['NamedMimePngEtxPngWithFolder']; - $media = $this->getPngWithFolder(); - - static::assertStringEndsWith('pngFileWithExtensionAndFolder.png', $media->getUrl()); - - $folderName = null; - if (\is_array($fixture['mediaFolder']) && !empty($fixture['mediaFolder']['name'])) { - $folderName = $fixture['mediaFolder']['name']; - } - - static::assertNotEmpty($folderName); - - $resource = fopen(TestController::TEST_FILE_PATH, 'rb'); - \assert($resource !== false); - - $filePath = $this->urlGenerator->getRelativeMediaUrl($media); - $fileSystem = $this->getPublicFilesystem(); - $fileSystem->writeStream($filePath, $resource); - - static::assertTrue($fileSystem->has($filePath)); - - $parameters = []; - $parameters['--folder-name'] = $folderName; - - $this->generateThumbnailsCommand->run(new ArrayInput($parameters, $this->generateThumbnailsCommand->getDefinition()), new NullOutput()); - - $this->runWorker(); - - $searchCriteria = new Criteria(); - $searchCriteria->setLimit(1); - $searchCriteria->addFilter(new EqualsFilter('media.id', $media->getId())); - $searchCriteria->addAssociation('mediaFolder.configuration.mediaThumbnailSizes'); - - $mediaResult = $this->mediaRepository->search($searchCriteria, $this->context); - - /** @var MediaEntity $updatedMedia */ - $updatedMedia = $mediaResult->getEntities()->first(); - - $thumbnails = $updatedMedia->getThumbnails(); - static::assertInstanceOf(MediaThumbnailCollection::class, $thumbnails); - static::assertEquals(2, $thumbnails->count()); - - foreach ($thumbnails as $thumbnail) { - $thumbnailUrl = $this->urlGenerator->getAbsoluteThumbnailUrl( - $media, - $thumbnail - ); - - static::assertStringStartsWith('http://127.0.0.1', $thumbnailUrl); - static::assertStringNotContainsString('thumbnail/', $thumbnailUrl); - static::assertFalse($fileSystem->has(\str_replace('media/', 'thumbnail/', $filePath))); - static::assertFalse($fileSystem->has($thumbnailUrl)); - } - } - - public function testMediaUrlWithActiveConfig(): void - { - $this->systemConfigService->set('FroshPlatformThumbnailProcessor.config.Active', true); - - $fixture = $this->mediaFixtures['NamedMimePngEtxPngWithFolder']; - $media = $this->getPngWithFolder(); - - static::assertStringEndsWith('pngFileWithExtensionAndFolder.png?width=3000', $media->getUrl()); - - $folderName = null; - if (\is_array($fixture['mediaFolder']) && !empty($fixture['mediaFolder']['name'])) { - $folderName = $fixture['mediaFolder']['name']; - } - - static::assertNotEmpty($folderName); - - $resource = fopen(TestController::TEST_FILE_PATH, 'rb'); - \assert($resource !== false); - - $filePath = $this->urlGenerator->getRelativeMediaUrl($media); - $fileSystem = $this->getPublicFilesystem(); - $fileSystem->writeStream($filePath, $resource); - - static::assertTrue($fileSystem->has($filePath)); - - $parameters = []; - $parameters['--folder-name'] = $folderName; - - $this->generateThumbnailsCommand->run(new ArrayInput($parameters, $this->generateThumbnailsCommand->getDefinition()), new NullOutput()); - - $this->runWorker(); - - $searchCriteria = new Criteria(); - $searchCriteria->setLimit(1); - $searchCriteria->addFilter(new EqualsFilter('media.id', $media->getId())); - $searchCriteria->addAssociation('mediaFolder.configuration.mediaThumbnailSizes'); - - $mediaResult = $this->mediaRepository->search($searchCriteria, $this->context); - - /** @var MediaEntity $updatedMedia */ - $updatedMedia = $mediaResult->getEntities()->first(); - - $thumbnails = $updatedMedia->getThumbnails(); - static::assertInstanceOf(MediaThumbnailCollection::class, $thumbnails); - static::assertEquals(2, $thumbnails->count()); - - foreach ($thumbnails as $thumbnail) { - $thumbnailUrl = $this->urlGenerator->getRelativeThumbnailUrl( - $media, - $thumbnail - ); - - static::assertStringStartsWith('http://127.0.0.1', $thumbnailUrl); - static::assertStringEndsWith('pngFileWithExtensionAndFolder.png?width=' . $thumbnail->getWidth(), $thumbnailUrl); - static::assertStringNotContainsString('thumbnail/', $thumbnailUrl); - - static::assertFalse($fileSystem->has(\str_replace('media/', 'thumbnail/', $filePath))); - static::assertFalse($fileSystem->has($thumbnailUrl)); - } - } -} diff --git a/tests/unit/Controller/Api/TestControllerTest.php b/tests/unit/Controller/Api/TestControllerTest.php deleted file mode 100644 index 00c4c52..0000000 --- a/tests/unit/Controller/Api/TestControllerTest.php +++ /dev/null @@ -1,399 +0,0 @@ -createMock(UrlGeneratorInterface::class); - $mediaRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - - $result = $controller->check($request, $dataBag); - static::assertSame('{"success":false}', $result->getContent()); - } - - public function testCheckFailsWithoutFile(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $mediaRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => null]); - - static::assertFileExists(TestController::TEST_FILE_PATH); - \rename(TestController::TEST_FILE_PATH, TestController::TEST_FILE_PATH . '.bak'); - static::assertFileDoesNotExist(TestController::TEST_FILE_PATH); - - static::expectException(\RuntimeException::class); - static::expectExceptionMessage(\sprintf('Test file at "%s" is missing or not readable', \realpath(TestController::TEST_FILE_PATH))); - $controller->check($request, $dataBag); - } - - public function testCheckFailsMediaSaving(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::never()) - ->method('getAbsoluteThumbnailUrl'); - - $mediaRepository = $this->createMock(EntityRepository::class); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::once()) - ->method('searchIds') - ->willReturn( - new IdSearchResult( - 1, - [['primaryKey' => 'folder-id', 'data' => []]], - new Criteria(), - Context::createDefaultContext(), - ), - ); - - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => null]); - - self::expectException(\RuntimeException::class); - self::expectExceptionMessage('Media has not been saved!'); - $controller->check($request, $dataBag); - } - - public function testCheckFailsProductMediaId(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::never()) - ->method('getAbsoluteThumbnailUrl'); - - $mediaRepository = $this->createMock(EntityRepository::class); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::once()) - ->method('searchIds') - ->willReturn( - new IdSearchResult( - 0, - [], - new Criteria(), - Context::createDefaultContext(), - ), - ); - - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => null]); - - self::expectException(\RuntimeException::class); - self::expectExceptionMessage('Media folder for product could not have been found!'); - $controller->check($request, $dataBag); - } - - public function testCheckWithExistingMedia(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::once()) - ->method('getAbsoluteThumbnailUrl') - ->willReturn('http://localhost/thumbnail.jpg?width=200'); - - $mediaRepository = $this->createMock(EntityRepository::class); - $mediaRepository->expects(static::once()) - ->method('search') - ->willReturnCallback(function (Criteria $criteria, Context $context): EntitySearchResult { - $collection = new EntityCollection(); - $media = new MediaEntity(); - $media->setId('test'); - $media->setFileExtension('jpg'); - $media->setFileName('test.jpg'); - $media->setMimeType('image/jpg'); - $media->setFileSize(100); - $media->setCreatedAt(new \DateTime()); - $media->setUpdatedAt(new \DateTime()); - $media->setMediaFolderId('test'); - $media->setMediaType(new ImageType()); - $collection->add($media); - - return new EntitySearchResult( - 'media_entity', - 1, - $collection, - null, - new Criteria(), - Context::createDefaultContext() - ); - }); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::never()) - ->method('searchIds') - ->willReturn( - new IdSearchResult( - 1, - [['primaryKey' => 'folder-id', 'data' => []]], - new Criteria(), - Context::createDefaultContext(), - ), - ); - - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => null]); - - $result = $controller->check($request, $dataBag); - assertSame('{"url":"http:\/\/localhost\/thumbnail.jpg?width=200"}', $result->getContent()); - } - - public function testCheck(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::once()) - ->method('getAbsoluteThumbnailUrl') - ->willReturn('http://localhost/thumbnail.jpg?width=200'); - - $mediaRepositoryResults = []; - $mediaRepositoryResults[] = new EntitySearchResult( - 'media_entity', - 0, - new EntityCollection(), - null, - new Criteria(), - Context::createDefaultContext() - ); - - $collection = new EntityCollection(); - $media = new MediaEntity(); - $media->setId('test'); - $media->setFileExtension('jpg'); - $media->setFileName('test.jpg'); - $media->setMimeType('image/jpg'); - $media->setFileSize(100); - $media->setCreatedAt(new \DateTime()); - $media->setUpdatedAt(new \DateTime()); - $media->setMediaFolderId('test'); - $media->setMediaType(new ImageType()); - $collection->add($media); - - $mediaRepositoryResults[] = new EntitySearchResult( - 'media_entity', - 1, - $collection, - null, - new Criteria(), - Context::createDefaultContext() - ); - - $matcher = static::exactly(2); - - $mediaRepository = $this->createMock(EntityRepository::class); - $mediaRepository->expects($matcher) - ->method('search') - ->willReturnCallback(function () use ($mediaRepositoryResults, $matcher): EntitySearchResult { - return match ($matcher->getInvocationCount()) { - 1 => $mediaRepositoryResults[0], - 2 => $mediaRepositoryResults[1], - default => throw new \RuntimeException('Unexpected invocation count'), - }; - }); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::once()) - ->method('searchIds') - ->willReturn( - new IdSearchResult( - 1, - [['primaryKey' => 'folder-id', 'data' => []]], - new Criteria(), - Context::createDefaultContext(), - ), - ); - - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => null]); - - $result = $controller->check($request, $dataBag); - assertSame('{"url":"http:\/\/localhost\/thumbnail.jpg?width=200"}', $result->getContent()); - } - - public function testCheckWithSalesChannel(): void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::once()) - ->method('getAbsoluteThumbnailUrl') - ->willReturn('http://localhost/thumbnail.jpg?width=200'); - - $mediaRepositoryResults = []; - $mediaRepositoryResults[] = new EntitySearchResult( - 'media_entity', - 0, - new EntityCollection(), - null, - new Criteria(), - Context::createDefaultContext() - ); - - $collection = new EntityCollection(); - $media = new MediaEntity(); - $media->setId('test'); - $media->setFileExtension('jpg'); - $media->setFileName('test.jpg'); - $media->setMimeType('image/jpg'); - $media->setFileSize(100); - $media->setCreatedAt(new \DateTime()); - $media->setUpdatedAt(new \DateTime()); - $media->setMediaFolderId('test'); - $media->setMediaType(new ImageType()); - $collection->add($media); - - $mediaRepositoryResults[] = new EntitySearchResult( - 'media_entity', - 1, - $collection, - null, - new Criteria(), - Context::createDefaultContext() - ); - - $matcher = static::exactly(2); - - $mediaRepository = $this->createMock(EntityRepository::class); - $mediaRepository->expects($matcher) - ->method('search') - ->willReturnCallback(function () use ($mediaRepositoryResults, $matcher): EntitySearchResult { - return match ($matcher->getInvocationCount()) { - 1 => $mediaRepositoryResults[0], - 2 => $mediaRepositoryResults[1], - default => throw new \RuntimeException('Unexpected invocation count'), - }; - }); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::once()) - ->method('searchIds') - ->willReturn( - new IdSearchResult( - 1, - [['primaryKey' => 'folder-id', 'data' => []]], - new Criteria(), - Context::createDefaultContext(), - ), - ); - - $fileSaver = $this->createMock(FileSaver::class); - $fileFetcher = $this->createMock(FileFetcher::class); - - $controller = new TestController( - $urlGenerator, - $mediaRepository, - $mediaFolderRepository, - $fileSaver, - $fileFetcher, - ); - - $request = new Request(); - $dataBag = new RequestDataBag(); - $dataBag->add(['salesChannelId' => '1111']); - - $result = $controller->check($request, $dataBag); - static::assertSame('{"url":"http:\/\/localhost\/thumbnail.jpg?width=200"}', $result->getContent()); - - static::assertTrue($request->attributes->has(TestController::REQUEST_ATTRIBUTE_TEST_ACTIVE)); - static::assertSame('1111', $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID)); - } -} diff --git a/tests/unit/EventListener/ThumbnailSizesChangedListenerTest.php b/tests/unit/EventListener/ThumbnailSizesChangedListenerTest.php deleted file mode 100644 index 1611dd0..0000000 --- a/tests/unit/EventListener/ThumbnailSizesChangedListenerTest.php +++ /dev/null @@ -1,88 +0,0 @@ - 'onThumbnailSizeChanged', - MediaFolderConfigurationMediaThumbnailSizeDefinition::ENTITY_NAME . '.deleted' => 'onThumbnailSizeChanged', - ], ThumbnailSizesChangedListener::getSubscribedEvents()); - } - - public function testOnThumbnailSizeChanged(): void - { - $generateThumbnailsCommand = $this->createMock(GenerateThumbnailsCommand::class); - $generateThumbnailsCommand->expects(static::once()) - ->method('run'); - - $generateThumbnailsCommand->expects(static::once()) - ->method('getDefinition') - ->willReturn((new GenerateThumbnailsCommand( - $this->createMock(ThumbnailService::class), - $this->createMock(EntityRepository::class), - $this->createMock(EntityRepository::class), - $this->createMock(MessageBus::class), - ))->getDefinition()); - - $mediaFolderEntity = new MediaFolderEntity(); - $mediaFolderEntity->setId('1111'); - $mediaFolderEntity->setName('Product Media'); - - $mediaFolderCollection = new MediaFolderCollection(); - $mediaFolderCollection->add($mediaFolderEntity); - - $mediaFolderRepository = new StaticEntityRepository([$mediaFolderCollection]); - - $thumbnailSizesChangedListener = new ThumbnailSizesChangedListener( - $generateThumbnailsCommand, - $mediaFolderRepository - ); - - $entityName = MediaFolderConfigurationMediaThumbnailSizeDefinition::ENTITY_NAME; - - $writtenResult = new EntityWriteResult('1111', ['mediaFolderConfigurationId' => '1111'], $entityName, EntityWriteResult::OPERATION_INSERT); - $writtenEvent = new EntityWrittenEvent($entityName, [$writtenResult], Context::createDefaultContext()); - - $thumbnailSizesChangedListener->onThumbnailSizeChanged($writtenEvent); - } - - public function testOnThumbnailSizeChangedWithEmptyPayload(): void - { - $generateThumbnailsCommand = $this->createMock(GenerateThumbnailsCommand::class); - $generateThumbnailsCommand->expects(static::never()) - ->method('run'); - - $mediaFolderRepository = $this->createMock(EntityRepository::class); - $mediaFolderRepository->expects(static::never()) - ->method('search'); - - $thumbnailSizesChangedListener = new ThumbnailSizesChangedListener( - $generateThumbnailsCommand, - $mediaFolderRepository - ); - - $entityName = MediaFolderConfigurationMediaThumbnailSizeDefinition::ENTITY_NAME; - - $writtenResult = new EntityWriteResult('1111', [], $entityName, EntityWriteResult::OPERATION_INSERT); - $writtenEvent = new EntityWrittenEvent($entityName, [$writtenResult], Context::createDefaultContext()); - - $thumbnailSizesChangedListener->onThumbnailSizeChanged($writtenEvent); - } -} diff --git a/tests/unit/FroshPlatformThumbnailProcessorTest.php b/tests/unit/FroshPlatformThumbnailProcessorTest.php deleted file mode 100644 index 96c07d6..0000000 --- a/tests/unit/FroshPlatformThumbnailProcessorTest.php +++ /dev/null @@ -1,27 +0,0 @@ -createMock(ContainerBuilder::class); - $container->expects(static::exactly(2)) - ->method('addCompilerPass'); - - $pluginBootstrap->build($container); - } - - public function testExecuteComposerCommands(): void - { - $pluginBootstrap = new FroshPlatformThumbnailProcessor(true, __DIR__ . '/../../'); - static::assertTrue($pluginBootstrap->executeComposerCommands()); - } -} diff --git a/tests/unit/Migration/Migration1686772873AddActiveConfigTest.php b/tests/unit/Migration/Migration1686772873AddActiveConfigTest.php deleted file mode 100644 index a018539..0000000 --- a/tests/unit/Migration/Migration1686772873AddActiveConfigTest.php +++ /dev/null @@ -1,110 +0,0 @@ -getCreationTimestamp()); - } - - public function testUpdate(): void - { - $migration = new Migration1686772873AddActiveConfig(); - $connection = $this->createMock(Connection::class); - - $queryBuilder = $this->createMock(QueryBuilder::class); - $queryBuilder->expects(static::once())->method('select')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('from')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('where')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('andWhere')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('setParameter')->willReturn($queryBuilder); - - $statement = $this->createMock(Result::class); - $statement->expects(static::once())->method('fetchOne')->willReturn('3.0.2'); - - $queryBuilder->expects(static::once())->method('executeQuery')->willReturn($statement); - - $connection->expects(static::once()) - ->method('createQueryBuilder') - ->willReturn($queryBuilder); - - $connection->expects(static::once()) - ->method('update'); - - $migration->update($connection); - } - - /** - * @dataProvider provideVersions - */ - public function testUpdateWithVersions(bool $runUpdate, ?string $version): void - { - $migration = new Migration1686772873AddActiveConfig(); - $connection = $this->createMock(Connection::class); - - $queryBuilder = $this->createMock(QueryBuilder::class); - $queryBuilder->expects(static::once())->method('select')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('from')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('where')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('andWhere')->willReturn($queryBuilder); - $queryBuilder->expects(static::once())->method('setParameter')->willReturn($queryBuilder); - - $statement = $this->createMock(Result::class); - $statement->expects(static::once())->method('fetchOne')->willReturn($version); - - $queryBuilder->expects(static::once())->method('executeQuery')->willReturn($statement); - - $connection->expects(static::once()) - ->method('createQueryBuilder') - ->willReturn($queryBuilder); - - $expects = $runUpdate ? static::once() : static::never(); - $connection->expects($expects) - ->method('update'); - - $migration->update($connection); - } - - public function testUpdateDestructive(): void - { - $migration = new Migration1686772873AddActiveConfig(); - $connection = $this->createMock(Connection::class); - $connection->expects(static::never()) - ->method('createQueryBuilder'); - $migration->updateDestructive($connection); - } - - /** - * @return iterable - */ - public static function provideVersions(): iterable - { - yield [false, null]; - yield [true, '2.0.0']; - yield [true, '2.0.1']; - yield [false, '2.1.0']; - yield [false, '2.1.1']; - yield [true, '3.0.0']; - yield [true, '3.0.1']; - yield [true, '3.0.2']; - yield [true, '3.0.3']; - yield [false, '3.0.4']; - yield [false, '3.1.0']; - yield [false, '3.1.1']; - yield [false, '3.1.2']; - yield [false, '3.1.3']; - yield [false, '3.1.4']; - yield [false, '3.1.5']; - yield [false, '3.1.6']; - yield [false, '3.1.7']; - } -} diff --git a/tests/unit/Service/ConfigReaderTest.php b/tests/unit/Service/ConfigReaderTest.php deleted file mode 100644 index 0347ab7..0000000 --- a/tests/unit/Service/ConfigReaderTest.php +++ /dev/null @@ -1,139 +0,0 @@ -set('FroshPlatformThumbnailProcessor.config.AnyString', 'test', $salesChannelId); - $systemConfigService->set('FroshPlatformThumbnailProcessor.config.AnyNumber', 5, $salesChannelId); - - $salesChannelIdDetector = $this->createMock(SalesChannelIdDetector::class); - $salesChannelIdDetector->expects(static::once()) - ->method('getSalesChannelId')->willReturn($salesChannelId); - - $class = new ConfigReader($systemConfigService, $salesChannelIdDetector, new RequestStack()); - - $anyStringConfig = $class->getConfig('AnyString'); - static::assertIsString($anyStringConfig); - static::assertSame('test', $anyStringConfig); - - $anyNumberConfig = $class->getConfig('AnyNumber'); - static::assertIsNumeric($anyNumberConfig); - static::assertSame(5, $anyNumberConfig); - } - - /** - * @dataProvider getWidths - */ - public function testGetConfigProcessOriginalImageMaxWidthAlwaysString(mixed $width): void - { - $systemConfigService = new StaticSystemConfigService(); - $systemConfigService->set('FroshPlatformThumbnailProcessor.config.ProcessOriginalImageMaxWidth', $width); - - $salesChannelIdDetector = $this->createMock(SalesChannelIdDetector::class); - $salesChannelIdDetector->expects(static::once()) - ->method('getSalesChannelId')->willReturn(null); - - $class = new ConfigReader($systemConfigService, $salesChannelIdDetector, new RequestStack()); - - $anyStringConfig = $class->getConfig('ProcessOriginalImageMaxWidth'); - static::assertIsString($anyStringConfig); - static::assertSame('300', $anyStringConfig); - } - - public function testGetConfigProcessOriginalImageMaxWidthFallbacksTo3000(): void - { - $systemConfigService = new StaticSystemConfigService(); - $systemConfigService->set('FroshPlatformThumbnailProcessor.config.ProcessOriginalImageMaxWidth', null); - - $salesChannelIdDetector = $this->createMock(SalesChannelIdDetector::class); - $salesChannelIdDetector->expects(static::once()) - ->method('getSalesChannelId')->willReturn(null); - - $class = new ConfigReader($systemConfigService, $salesChannelIdDetector, new RequestStack()); - - $anyStringConfig = $class->getConfig('ProcessOriginalImageMaxWidth'); - static::assertIsString($anyStringConfig); - static::assertSame('3000', $anyStringConfig); - } - - /** - * @dataProvider getActiveValues - */ - public function testGetConfigActive(mixed $value): void - { - $systemConfigService = new StaticSystemConfigService(); - $systemConfigService->set('FroshPlatformThumbnailProcessor.config.Active', $value); - - $salesChannelIdDetector = $this->createMock(SalesChannelIdDetector::class); - $salesChannelIdDetector->expects(static::once()) - ->method('getSalesChannelId')->willReturn(null); - - $class = new ConfigReader($systemConfigService, $salesChannelIdDetector, new RequestStack()); - - static::assertTrue($class->getConfig('Active')); - } - - public function testGetConfigActiveWithActiveTest(): void - { - $systemConfigService = new StaticSystemConfigService(); - $systemConfigService->set('FroshPlatformThumbnailProcessor.config.Active', false); - - $salesChannelIdDetector = $this->createMock(SalesChannelIdDetector::class); - $salesChannelIdDetector->expects(static::once()) - ->method('getSalesChannelId')->willReturn(null); - - $requestStack = new RequestStack(); - $requestStack->push(new Request(attributes: [TestController::REQUEST_ATTRIBUTE_TEST_ACTIVE => true])); - - $class = new ConfigReader($systemConfigService, $salesChannelIdDetector, $requestStack); - - static::assertTrue($class->getConfig('Active')); - } - - /** - * @return iterable - */ - public static function getSalesChannelIds(): iterable - { - yield [null]; - yield [Uuid::randomHex()]; - yield [Uuid::randomHex()]; - yield [Uuid::randomHex()]; - yield [Uuid::randomHex()]; - } - - /** - * @return iterable - */ - public static function getWidths(): iterable - { - yield [300]; - yield ['300']; - yield [300.00]; - } - - /** - * @return iterable - */ - public function getActiveValues(): iterable - { - yield [null]; - yield [true]; - } -} diff --git a/tests/unit/Service/SalesChannelIdDetectorTest.php b/tests/unit/Service/SalesChannelIdDetectorTest.php deleted file mode 100644 index fdd6782..0000000 --- a/tests/unit/Service/SalesChannelIdDetectorTest.php +++ /dev/null @@ -1,116 +0,0 @@ - 'mySalesChannelId', - ], - ); - - $requestStack->push($mainRequest); - $requestStack->push(new Request(['foo' => 'bar'])); - - $productExportRepository = $this->createMock(EntityRepository::class); - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertSame('mySalesChannelId', $class->getSalesChannelId()); - } - - public function testGetSalesChannelIdWithoutMainRequest(): void - { - $requestStack = new RequestStack(); - - $productExportRepository = $this->createMock(EntityRepository::class); - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertNull($class->getSalesChannelId()); - } - - public function testGetSalesChannelIdNull(): void - { - $requestStack = new RequestStack(); - $requestStack->push(new Request()); - - $productExportRepository = $this->createMock(EntityRepository::class); - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertNull($class->getSalesChannelId()); - } - - public function testGetSalesChannelId(): void - { - $requestStack = new RequestStack(); - $requestStack->push(new Request( - attributes: [ - PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID => 'mySalesChannelId', - ], - )); - - $productExportRepository = $this->createMock(EntityRepository::class); - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertSame('mySalesChannelId', $class->getSalesChannelId()); - } - - public function testGetSalesChannelIdProductExport(): void - { - $requestStack = new RequestStack(); - $requestStack->push(new Request( - attributes: [ - '_route' => 'store-api.product.export', - 'fileName' => 'anyFilename', - 'accessKey' => 'anyAccessKey', - ], - )); - - $productExportEntity = new ProductExportEntity(); - $productExportEntity->setId(Uuid::randomHex()); - $productExportEntity->setFileName('anyFilename'); - $productExportEntity->setAccessKey('anyAccessKey'); - $productExportEntity->setSalesChannelId('myCoolSalesChannelId'); - - $productExportCollection = new ProductExportCollection(); - $productExportCollection->add($productExportEntity); - - $productExportRepository = new StaticEntityRepository([$productExportCollection]); - - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertSame('myCoolSalesChannelId', $class->getSalesChannelId()); - } - - public function testGetSalesChannelIdProductExportWithoutFileNameAndAccessKey(): void - { - $requestStack = new RequestStack(); - $requestStack->push(new Request( - attributes: [ - '_route' => 'store-api.product.export', - ], - )); - - $productExportRepository = $this->createMock(StaticEntityRepository::class); - $productExportRepository->expects(static::never())->method('search'); - - $class = new SalesChannelIdDetector($requestStack, $productExportRepository); - - static::assertNull($class->getSalesChannelId()); - } -} diff --git a/tests/unit/Service/ThumbnailUrlTemplateTest.php b/tests/unit/Service/ThumbnailUrlTemplateTest.php deleted file mode 100644 index 3f9a528..0000000 --- a/tests/unit/Service/ThumbnailUrlTemplateTest.php +++ /dev/null @@ -1,73 +0,0 @@ -createMock(ConfigReader::class); - $configReader->expects(static::once()) - ->method('getConfig')->willReturn('{mediaUrl}/{mediaPath}?width={width}&uff'); - - $class = new ThumbnailUrlTemplate($configReader); - - $url = $class->getUrl($mediaUrl, $mediaPath, $width); - - static::assertSame(\sprintf('%s/%s?width=%s&uff', $mediaUrl, $mediaPath, $width), $url); - } - - /** - * @dataProvider getSalesChannelIds - */ - public function testGetUrlWithoutSetConfig(?string $salesChannelId, string $mediaUrl, string $mediaPath, string $width): void - { - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::once()) - ->method('getConfig')->willReturn('{mediaUrl}/{mediaPath}?width={width}'); - - $class = new ThumbnailUrlTemplate($configReader); - - $url = $class->getUrl($mediaUrl, $mediaPath, $width); - - static::assertSame(\sprintf('%s/%s?width=%s', $mediaUrl, $mediaPath, $width), $url); - } - - /** - * @dataProvider getSalesChannelIds - */ - public function testGetUrlGetPatternOnce(?string $salesChannelId, string $mediaUrl, string $mediaPath, string $width): void - { - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::once()) - ->method('getConfig')->willReturn('{mediaUrl}/{mediaPath}?width={width}'); - - $class = new ThumbnailUrlTemplate($configReader); - - $class->getUrl($mediaUrl, $mediaPath, $width); - - $url = $class->getUrl($mediaUrl, $mediaPath, $width); - - static::assertSame(\sprintf('%s/%s?width=%s', $mediaUrl, $mediaPath, $width), $url); - } - - /** - * @return iterable - */ - public static function getSalesChannelIds(): iterable - { - yield [null, 'https://www.anywebpage.test', 'media/78/a1/myimage.jpg', '200']; - yield [Uuid::randomHex(), 'https://www.anyotherwebpage.test', 'media/aa/a1/myimage.jpg', '300']; - yield [Uuid::randomHex(), 'https://www.anyother2webpage.test', 'media/aa/bb/myimage.jpg', '700']; - yield [Uuid::randomHex(), 'https://www.anyother3webpage.test', 'media/aa/cc/myimage.jpg', '900']; - yield [Uuid::randomHex(), 'https://www.anyother4webpage.test', 'media/aa/dd/myimage.jpg', '1000']; - } -} diff --git a/tests/unit/Service/UrlGeneratorDecoratorTest.php b/tests/unit/Service/UrlGeneratorDecoratorTest.php deleted file mode 100644 index 7ab63f5..0000000 --- a/tests/unit/Service/UrlGeneratorDecoratorTest.php +++ /dev/null @@ -1,383 +0,0 @@ -mockMediaThumbnailData( - decoratedUrlGenerator: $this->getDefaultDecoratedUrlGenerator() - ); - - $result = $urlGenerator->getRelativeThumbnailUrl($media, $thumbnail); - - static::assertSame('https://any.url/aa/bb/cc.jpg?width=100', $result); - } - - public function testGetAbsoluteMediaUrl(): void - { - [$urlGenerator, $media] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $this->getDefaultDecoratedUrlGenerator() - ); - - $result = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame('https://any.url/aa/bb/cc.jpg?width=100', $result); - } - - public function testGetAbsoluteMediaUrlWithInactiveConfig(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::once()) - ->method('getAbsoluteMediaUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg'); - - [$urlGenerator, $media] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $decoratedUrlGenerator, - pluginConfig: ['Active' => false] - ); - - $result = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame('https://any.url/aa/bb/cc.jpg', $result); - } - - public function testGetAbsoluteMediaUrlWithUnsupportedFileType(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::once()) - ->method('getAbsoluteMediaUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg'); - - [$urlGenerator, $media] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $decoratedUrlGenerator, - pluginConfig: ['ExtensionsAllowList' => 'png, gif'] - ); - - $result = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame('https://any.url/aa/bb/cc.jpg', $result); - } - - public function testGetAbsoluteMediaUrlWithCachedExtensionsAllowList(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::never()) - ->method('getAbsoluteMediaUrl'); - - $thumbnailUrlTemplate = $this->createMock(ThumbnailUrlTemplateInterface::class); - $thumbnailUrlTemplate->expects(static::any()) - ->method('getUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg?width=100'); - - $fileSystem = new Filesystem(new InMemoryFilesystemAdapter(), ['public_url' => 'https://any.url']); - - $defaultPluginConfig = [ - 'Active' => true, - 'ProcessOriginalImageMaxWidth' => '3000', - 'ExtensionsAllowList' => 'jpg', - 'ThumbnailPattern' => '{mediaUrl}/{mediaPath}?width={width}', - ]; - - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::exactly(5)) - ->method('getConfig') - ->willReturnCallback(function ($key) use ($defaultPluginConfig) { - return $defaultPluginConfig[$key] ?? null; - }); - - $urlGenerator = new UrlGeneratorDecorator( - $decoratedUrlGenerator, - $thumbnailUrlTemplate, - $fileSystem, - $configReader - ); - - $media = $this->createMediaEntity(); - - $first = $urlGenerator->getAbsoluteMediaUrl($media); - $second = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame($first, $second); - } - - public function testGetAbsoluteMediaUrlWithEmptyFileExtension(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::once()) - ->method('getAbsoluteMediaUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg'); - - $thumbnailUrlTemplate = $this->createMock(ThumbnailUrlTemplateInterface::class); - $thumbnailUrlTemplate->expects(static::any()) - ->method('getUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg?width=100'); - - $fileSystem = new Filesystem(new InMemoryFilesystemAdapter(), ['public_url' => 'https://any.url']); - - $defaultPluginConfig = [ - 'Active' => true, - 'ProcessOriginalImageMaxWidth' => '3000', - 'ExtensionsAllowList' => 'jpg', - 'ThumbnailPattern' => '{mediaUrl}/{mediaPath}?width={width}', - ]; - - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::once()) - ->method('getConfig') - ->willReturnCallback(function ($key) use ($defaultPluginConfig) { - return $defaultPluginConfig[$key] ?? null; - }); - - $urlGenerator = new UrlGeneratorDecorator( - $decoratedUrlGenerator, - $thumbnailUrlTemplate, - $fileSystem, - $configReader - ); - - $mediaWithoutfileExtension = new MediaEntity(); - $mediaWithoutfileExtension->setId('test'); - $mediaWithoutfileExtension->setFileName('test.jpg'); - $mediaWithoutfileExtension->setMimeType('image/jpg'); - $mediaWithoutfileExtension->setFileSize(100); - $mediaWithoutfileExtension->setCreatedAt(new \DateTime()); - $mediaWithoutfileExtension->setUpdatedAt(new \DateTime()); - $mediaWithoutfileExtension->setMediaFolderId('test'); - $mediaWithoutfileExtension->setMediaType(new ImageType()); - - $result = $urlGenerator->getAbsoluteMediaUrl($mediaWithoutfileExtension); - - static::assertSame('https://any.url/aa/bb/cc.jpg', $result); - } - - public function testGetAbsoluteMediaUrlWithEmptyExtensionsAllowList(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::once()) - ->method('getAbsoluteMediaUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg'); - - $thumbnailUrlTemplate = $this->createMock(ThumbnailUrlTemplateInterface::class); - $thumbnailUrlTemplate->expects(static::any()) - ->method('getUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg?width=100'); - - $fileSystem = new Filesystem(new InMemoryFilesystemAdapter(), ['public_url' => 'https://any.url']); - - $defaultPluginConfig = [ - 'Active' => true, - 'ProcessOriginalImageMaxWidth' => '3000', - 'ExtensionsAllowList' => '', - 'ThumbnailPattern' => '{mediaUrl}/{mediaPath}?width={width}', - ]; - - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::any()) - ->method('getConfig') - ->willReturnCallback(function ($key) use ($defaultPluginConfig) { - return $defaultPluginConfig[$key] ?? null; - }); - - $urlGenerator = new UrlGeneratorDecorator( - $decoratedUrlGenerator, - $thumbnailUrlTemplate, - $fileSystem, - $configReader - ); - - $media = $this->createMediaEntity(); - - $result = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame('https://any.url/aa/bb/cc.jpg', $result); - } - - public function testGetRelativeMediaUrl(): void - { - static::assertTrue(true); - [$urlGenerator, $media] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $this->getDefaultDecoratedUrlGenerator() - ); - - $result = $urlGenerator->getRelativeMediaUrl($media); - - static::assertSame('aa/bb/cc.jpg', $result); - } - - public function testGetAbsoluteThumbnailUrl(): void - { - [$urlGenerator, $media, $thumbnail] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $this->getDefaultDecoratedUrlGenerator() - ); - - $result = $urlGenerator->getAbsoluteThumbnailUrl($media, $thumbnail); - - static::assertSame('https://any.url/aa/bb/cc.jpg?width=100', $result); - } - - public function testGetAbsoluteThumbnailUrlWithInactiveConfig(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::once()) - ->method('getAbsoluteMediaUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg'); - - [$urlGenerator, $media, $thumbnail] = $this->mockMediaThumbnailData( - decoratedUrlGenerator: $decoratedUrlGenerator, - pluginConfig: ['Active' => false] - ); - - $result = $urlGenerator->getAbsoluteThumbnailUrl($media, $thumbnail); - - static::assertSame('https://any.url/aa/bb/cc.jpg', $result); - } - - public function testReset(): void - { - $decoratedUrlGenerator = $this->createMock(UrlGeneratorInterface::class); - $decoratedUrlGenerator->expects(static::never()) - ->method('getAbsoluteMediaUrl'); - - $thumbnailUrlTemplate = $this->createMock(ThumbnailUrlTemplateInterface::class); - $thumbnailUrlTemplate->expects(static::exactly(2)) - ->method('getUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg?width=100'); - - $fileSystem = new Filesystem(new InMemoryFilesystemAdapter(), ['public_url' => 'https://any.url']); - - $defaultPluginConfig = [ - 'Active' => true, - 'ProcessOriginalImageMaxWidth' => '3000', - 'ExtensionsAllowList' => 'jpg', - 'ThumbnailPattern' => '{mediaUrl}/{mediaPath}?width={width}', - ]; - - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::exactly(6)) - ->method('getConfig') - ->willReturnCallback(function ($key) use ($defaultPluginConfig) { - return $defaultPluginConfig[$key] ?? null; - }); - - $urlGenerator = new UrlGeneratorDecorator( - $decoratedUrlGenerator, - $thumbnailUrlTemplate, - $fileSystem, - $configReader - ); - - $media = $this->createMediaEntity(); - - $first = $urlGenerator->getAbsoluteMediaUrl($media); - $urlGenerator->reset(); - $second = $urlGenerator->getAbsoluteMediaUrl($media); - - static::assertSame($first, $second); - } - - /** - * @param array|null $pluginConfig - * - * @return array{UrlGeneratorDecorator, MediaEntity, MediaThumbnailEntity} - */ - private function mockMediaThumbnailData(UrlGeneratorInterface $decoratedUrlGenerator, ?array $pluginConfig = null): array - { - $defaultPluginConfig = [ - 'Active' => true, - 'ProcessOriginalImageMaxWidth' => '3000', - 'ExtensionsAllowList' => 'jpg', - 'ThumbnailPattern' => '{mediaUrl}/{mediaPath}?width={width}', - ]; - - if (\is_array($pluginConfig)) { - $defaultPluginConfig = \array_merge($defaultPluginConfig, $pluginConfig); - } - - $thumbnailUrlTemplate = $this->createMock(ThumbnailUrlTemplateInterface::class); - $thumbnailUrlTemplate->expects(static::any()) - ->method('getUrl') - ->willReturn('https://any.url/aa/bb/cc.jpg?width=100'); - - $requestStack = new RequestStack(); - $requestStack->push(new Request()); - - $fileSystem = new Filesystem(new InMemoryFilesystemAdapter(), ['public_url' => 'https://any.url']); - - $configReader = $this->createMock(ConfigReader::class); - $configReader->expects(static::any()) - ->method('getConfig') - ->willReturnCallback(function ($key) use ($defaultPluginConfig) { - return $defaultPluginConfig[$key] ?? null; - }); - - $urlGenerator = new UrlGeneratorDecorator( - $decoratedUrlGenerator, - $thumbnailUrlTemplate, - $fileSystem, - $configReader - ); - - $media = $this->createMediaEntity(); - $thumbnail = $this->createThumbnailEntity(); - - return [$urlGenerator, $media, $thumbnail]; - } - - private function createMediaEntity(): MediaEntity - { - $media = new MediaEntity(); - $media->setId('test'); - $media->setFileExtension('jpg'); - $media->setFileName('test.jpg'); - $media->setMimeType('image/jpg'); - $media->setFileSize(100); - $media->setCreatedAt(new \DateTime()); - $media->setUpdatedAt(new \DateTime()); - $media->setMediaFolderId('test'); - $media->setMediaType(new ImageType()); - - return $media; - } - - private function createThumbnailEntity(): MediaThumbnailEntity - { - $thumbnail = new MediaThumbnailEntity(); - $thumbnail->setId('test'); - $thumbnail->setMediaId('test'); - $thumbnail->setMedia($this->createMediaEntity()); - $thumbnail->setWidth(100); - $thumbnail->setHeight(100); - $thumbnail->setCreatedAt(new \DateTime()); - $thumbnail->setUpdatedAt(new \DateTime()); - - return $thumbnail; - } - - private function getDefaultDecoratedUrlGenerator(): UrlGeneratorInterface - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $urlGenerator->expects(static::never()) - ->method('getAbsoluteMediaUrl') - ->willReturn(''); - $urlGenerator->expects(static::any()) - ->method('getRelativeMediaUrl') - ->willReturn('aa/bb/cc.jpg'); - - return $urlGenerator; - } -}