[haiku-depot-web] [haiku-depot-web-app] 6 new revisions pushed by haiku.li...@xxxxxxxxx on 2014-04-09 11:05 GMT

  • From: haiku-depot-web-app@xxxxxxxxxxxxxx
  • To: haiku-depot-web@xxxxxxxxxxxxx
  • Date: Wed, 09 Apr 2014 11:05:53 +0000

master moved from 093618709ea2 to b4e575bb22f3

6 new revisions:

Revision: d67a2c24d497
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Fri Apr  4 10:13:57 2014 UTC
Log: + api changes to support localization of package summary and descripti...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=d67a2c24d497

Revision: 2fbe296f3f5d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Apr  5 10:49:12 2014 UTC
Log: + api changes to support extraction of localization data for a pkg ver...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=2fbe296f3f5d

Revision: 881e18f28a12
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Apr  7 11:46:03 2014 UTC
Log: + changes to support sensible up-take of natural language localization...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=881e18f28a12

Revision: 71a22530b2ca
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Tue Apr  8 10:24:23 2014 UTC
Log:      + initial work on package version localization
http://code.google.com/p/haiku-depot-web-app/source/detail?r=71a22530b2ca

Revision: 5dd8c8e0ff1d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Wed Apr  9 11:01:12 2014 UTC
Log: + work on a bulk retreival system for the haiku depot server desktop c...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=5dd8c8e0ff1d

Revision: b4e575bb22f3
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Wed Apr  9 11:01:59 2014 UTC
Log:      + correct spelling mistake in packaging
http://code.google.com/p/haiku-depot-web-app/source/detail?r=b4e575bb22f3

==============================================================================
Revision: d67a2c24d497
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Fri Apr  4 10:13:57 2014 UTC
Log: + api changes to support localization of package summary and description

http://code.google.com/p/haiku-depot-web-app/source/detail?r=d67a2c24d497

Added:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationResult.java
Modified:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgcategoriescontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java

=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationRequest.java Fri Apr 4 10:13:57 2014 UTC
@@ -0,0 +1,20 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+public class UpdatePkgVersionLocalizationRequest {
+
+    public String pkgName;
+
+    public String naturalLanguageCode;
+
+    public String architectureCode;
+
+    public String description;
+
+    public String summary;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationResult.java Fri Apr 4 10:13:57 2014 UTC
@@ -0,0 +1,9 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+public class UpdatePkgVersionLocalizationResult {
+}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Fri Apr 4 10:13:57 2014 UTC
@@ -89,4 +89,14 @@

ReorderPkgScreenshotsResult reorderPkgScreenshots(ReorderPkgScreenshotsRequest reorderPkgScreenshotsRequest) throws ObjectNotFoundException;

+    /**
+ * <p>This method will set the localized text stored against the <strong>latest</strong> version of the nominated + * package. If the package cannot be found then this method will throw an instance of + * {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException}. It is not possible to update the + * localization on an older version and all fields are required. It is not possible to edit the English
+     * localization.</p>
+     */
+
+ UpdatePkgVersionLocalizationResult updatePkgVersionLocalization(UpdatePkgVersionLocalizationRequest updatePkgVersionLocalizationRequest) throws ObjectNotFoundException;
+
 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java Wed Mar 12 09:41:30 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java Fri Apr 4 10:13:57 2014 UTC
@@ -28,7 +28,8 @@
* seen. Do not use this unless the user is being displayed a user-interface of the package so that they * have <em>really</em> seen it. This value may be supplied as null. This only applies when the * {@link org.haikuos.haikudepotserver.api1.model.pkg.GetPkgRequest.VersionType#LATEST} version type is
-     * being requested.</p>
+ * being requested. Also note that the system has a feature to avoid double counting from the same address in
+     * quick succession.</p>
      */

     public Boolean incrementViewCounter;
@@ -42,6 +43,6 @@

     public VersionType versionType;

-    // TODO - natural language
+    public String naturalLanguageCode;

 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java Wed Mar 12 09:41:30 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java Fri Apr 4 10:13:57 2014 UTC
@@ -37,6 +37,15 @@
         public String architectureCode;
         public String summary;
         public String description;
+
+        /**
+ * <p>In the request the client may have requested a specific natural language, but that may not have been + * available. This code indicates the natural language code that was <em>actually</em> used to obtain
+         * material such as the summary and the description.</p>
+         */
+
+        public String naturalLanguageCode;
+
         public String repositoryCode;

         public List<String> licenses;
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Fri Apr 4 10:13:57 2014 UTC
@@ -204,7 +204,7 @@

// if there are more than we asked for then there must be more available.

- result.hasMore = new Boolean(searchedPkgVersions.size() > request.limit);
+        result.hasMore = searchedPkgVersions.size() > request.limit;

         if(result.hasMore) {
searchedPkgVersions = searchedPkgVersions.subList(0,request.limit);
@@ -245,7 +245,7 @@
* <p>Given the persistence model object, this method will construct the DTO to be sent back over the wire.</p>
      */

-    private GetPkgResult.Version createVersion(PkgVersion pkgVersion) {
+ private GetPkgResult.Version createVersion(PkgVersion pkgVersion, NaturalLanguage naturalLanguage) {
         GetPkgResult.Version version = new GetPkgResult.Version();

         version.major = pkgVersion.getMajor();
@@ -276,11 +276,18 @@

         version.viewCounter = pkgVersion.getViewCounter();

-        // TODO - languages
-        if(!pkgVersion.getPkgVersionLocalizations().isEmpty()) {
- PkgVersionLocalization pkgVersionLocalization = pkgVersion.getPkgVersionLocalizations().get(0);
-            version.description = pkgVersionLocalization.getDescription();
-            version.summary = pkgVersionLocalization.getSummary();
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersion.getPkgVersionLocalization(naturalLanguage);
+
+        if(!pkgVersionLocalizationOptional.isPresent()) {
+ if(!naturalLanguage.getCode().equals(NaturalLanguage.CODE_ENGLISH)) { + pkgVersionLocalizationOptional = pkgVersion.getPkgVersionLocalization(NaturalLanguage.CODE_ENGLISH);
+            }
+        }
+
+        if(pkgVersionLocalizationOptional.isPresent()) {
+ version.description = pkgVersionLocalizationOptional.get().getDescription(); + version.summary = pkgVersionLocalizationOptional.get().getSummary(); + version.naturalLanguageCode = pkgVersionLocalizationOptional.get().getNaturalLanguage().getCode();
         }

         version.urls = Lists.transform(
@@ -304,6 +311,7 @@
         Preconditions.checkNotNull(request);
         Preconditions.checkState(!Strings.isNullOrEmpty(request.name));
         Preconditions.checkNotNull(request.versionType);
+ Preconditions.checkState(!Strings.isNullOrEmpty(request.naturalLanguageCode));

         final ObjectContext context = serverRuntime.getContext();

@@ -312,6 +320,12 @@
         if(!Strings.isNullOrEmpty(request.architectureCode)) {
architectureOptional = Architecture.getByCode(context, request.architectureCode);
         }
+
+ Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, request.naturalLanguageCode);
+
+        if(!naturalLanguageOptional.isPresent()) {
+ throw new ObjectNotFoundException(NaturalLanguage.class.getSimpleName(), request.naturalLanguageCode);
+        }

         GetPkgResult result = new GetPkgResult();
         Pkg pkg = getPkg(context, request.name);
@@ -334,20 +348,23 @@
Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
                         context,
                         pkg,
-                        Lists.newArrayList(
+                        ImmutableList.of(
                                 architectureOptional.get(),
Architecture.getByCode(context, Architecture.CODE_ANY).get(), - Architecture.getByCode(context, Architecture.CODE_SOURCE).get())); + Architecture.getByCode(context, Architecture.CODE_SOURCE).get())
+                );

                 if(!pkgVersionOptional.isPresent()) {
- throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), request.name);
+                    throw new ObjectNotFoundException(
+                            PkgVersion.class.getSimpleName(),
+                            request.name);
                 }

if(null!=request.incrementViewCounter && request.incrementViewCounter) {

                     String cacheKey = null;
                     String remoteIdentifier = getRemoteIdentifier();
-                    boolean shouldIncrement = false;
+                    boolean shouldIncrement;

if(shouldProtectPkgVersionViewCounterFromRecurringIncrementFromSameClient && !Strings.isNullOrEmpty(remoteIdentifier)) { Long pkgVersionId = (Long) pkgVersionOptional.get().getObjectId().getIdSnapshot().get(PkgVersion.ID_PK_COLUMN);
@@ -378,7 +395,9 @@

                 }

- result.versions = Collections.singletonList(createVersion(pkgVersionOptional.get()));
+                result.versions = Collections.singletonList(createVersion(
+                        pkgVersionOptional.get(),
+                        naturalLanguageOptional.get()));

                 break;

@@ -671,4 +690,60 @@

         return new ReorderPkgScreenshotsResult();
     }
+
+    @Override
+    public UpdatePkgVersionLocalizationResult updatePkgVersionLocalization(
+ UpdatePkgVersionLocalizationRequest updatePkgVersionLocalizationRequest) throws ObjectNotFoundException {
+
+        Preconditions.checkNotNull(updatePkgVersionLocalizationRequest);
+ Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.description)); + Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.naturalLanguageCode)); + Preconditions.checkState(!updatePkgVersionLocalizationRequest.naturalLanguageCode.equals(NaturalLanguage.CODE_ENGLISH)); + Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.pkgName)); + Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.summary));
+
+        final ObjectContext context = serverRuntime.getContext();
+ Pkg pkg = getPkg(context, updatePkgVersionLocalizationRequest.pkgName);
+
+ Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, updatePkgVersionLocalizationRequest.naturalLanguageCode);
+
+        if(!naturalLanguageOptional.isPresent()) {
+ throw new ObjectNotFoundException(NaturalLanguage.class.getSimpleName(), updatePkgVersionLocalizationRequest.naturalLanguageCode);
+        }
+
+ Optional<Architecture> architectureOptional = Architecture.getByCode(context, updatePkgVersionLocalizationRequest.architectureCode);
+
+        if(!architectureOptional.isPresent()) {
+ throw new ObjectNotFoundException(Architecture.class.getSimpleName(), updatePkgVersionLocalizationRequest.architectureCode);
+        }
+
+ Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(context, pkg, Collections.singletonList(architectureOptional.get()));
+
+        if(!pkgVersionOptional.isPresent()) {
+ throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), pkg.getName() + "/" + architectureOptional.get().getCode());
+        }
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageOptional.get());
+
+        if(!pkgVersionLocalizationOptional.isPresent()) {
+ PkgVersionLocalization newPkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + newPkgVersionLocalization.setNaturalLanguage(naturalLanguageOptional.get()); + pkgVersionOptional.get().addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, newPkgVersionLocalization, true); + pkgVersionLocalizationOptional = Optional.of(newPkgVersionLocalization);
+        }
+
+ pkgVersionLocalizationOptional.get().setSummary(updatePkgVersionLocalizationRequest.summary); + pkgVersionLocalizationOptional.get().setDescription(updatePkgVersionLocalizationRequest.description);
+
+        context.commitChanges();
+
+ logger.info("did update the localization for pkg {} in architecture {} for natural language {}",new Object[] {
+                pkg.getName(),
+                architectureOptional.get().getCode(),
+                naturalLanguageOptional.get().getCode()
+        });
+
+        return new UpdatePkgVersionLocalizationResult();
+    }
+
 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Fri Apr 4 10:13:57 2014 UTC
@@ -27,6 +27,7 @@

     public final static String CODE_ENGLISH = "en";
     public final static String CODE_GERMAN = "de";
+    public final static String CODE_SPANISH = "es";

     public static List<NaturalLanguage> getAll(ObjectContext context) {
         Preconditions.checkNotNull(context);
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Wed Mar 12 11:16:46 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Fri Apr 4 10:13:57 2014 UTC
@@ -7,6 +7,8 @@

 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import org.apache.cayenne.ObjectContext;
@@ -125,5 +127,33 @@
     public void incrementViewCounter() {
         setViewCounter(getViewCounter()+1);
     }
+
+    /**
+ * <p>This will try to find localized data for the pkg version for the supplied natural language. Because + * English language data is hard-coded into the package payload, english will always be available.</p>
+     */
+
+ public Optional<PkgVersionLocalization> getPkgVersionLocalization(final NaturalLanguage naturalLanguage) {
+        return getPkgVersionLocalization(naturalLanguage.getCode());
+    }
+
+    /**
+ * <p>This will try to find localized data for the pkg version for the supplied natural language. Because + * English language data is hard-coded into the package payload, english will always be available.</p>
+     */
+
+ public Optional<PkgVersionLocalization> getPkgVersionLocalization(final String naturalLanguageCode) { + Preconditions.checkState(!Strings.isNullOrEmpty(naturalLanguageCode));
+
+        return Iterables.tryFind(
+                getPkgVersionLocalizations(),
+                new Predicate<PkgVersionLocalization>() {
+                    @Override
+                    public boolean apply(PkgVersionLocalization input) {
+ return input.getNaturalLanguage().getCode().equals(naturalLanguageCode);
+                    }
+                }
+        );
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgcategoriescontroller.js Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgcategoriescontroller.js Fri Apr 4 10:13:57 2014 UTC
@@ -8,11 +8,11 @@
     [
         '$scope','$log','$location','$routeParams',
         'jsonRpc','constants','errorHandling',
-        'breadcrumbs','referenceData',
+        'breadcrumbs','referenceData','userState',
         function(
             $scope,$log,$location,$routeParams,
             jsonRpc,constants,errorHandling,
-            breadcrumbs,referenceData) {
+            breadcrumbs,referenceData,userState) {

// this is the maximum number of categories in which a package may be registered. This // is just enforced in the user interface for practicalities sake.
@@ -33,11 +33,12 @@
             function refetchPkg() {
                 jsonRpc.call(
                     constants.ENDPOINT_API_V1_PKG,
-                    "getPkg",
+                    'getPkg',
                     [{
                         name: $routeParams.name,
                         versionType: 'NONE',
- architectureCode: undefined // not required if we don't need the version + architectureCode: undefined, // not required if we don't need the version + naturalLanguageCode: userState.naturalLanguageCode()
                     }]
                 ).then(
                     function(result) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Fri Apr 4 10:13:57 2014 UTC
@@ -8,11 +8,11 @@
     [
         '$scope','$log','$location','$routeParams',
         'jsonRpc','constants','pkgIcon','errorHandling',
-        'breadcrumbs',
+        'breadcrumbs','userState',
         function(
             $scope,$log,$location,$routeParams,
             jsonRpc,constants,pkgIcon,errorHandling,
-            breadcrumbs) {
+            breadcrumbs,userState) {

             var ICON_SIZE_LIMIT = 100 * 1024; // 100k

@@ -38,11 +38,12 @@
             function refetchPkg() {
                 jsonRpc.call(
                         constants.ENDPOINT_API_V1_PKG,
-                        "getPkg",
+                        'getPkg',
                         [{
                             name: $routeParams.name,
                             versionType: 'NONE',
- architectureCode: undefined // not required if we don't need the version + architectureCode: undefined, // not required if we don't need the version + naturalLanguageCode: userState.naturalLanguageCode()
                         }]
                     ).then(
                     function(result) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Fri Apr 4 10:13:57 2014 UTC
@@ -8,11 +8,11 @@
     [
         '$scope','$log','$location','$routeParams',
         'jsonRpc','constants','pkgScreenshot','errorHandling',
-        'breadcrumbs',
+        'breadcrumbs','userState',
         function(
             $scope,$log,$location,$routeParams,
             jsonRpc,constants,pkgScreenshot,errorHandling,
-            breadcrumbs) {
+            breadcrumbs,userState) {

// the upload size must be less than this or it is too big for the
             // far end to process.
@@ -72,11 +72,12 @@
             function refetchPkg() {
                 jsonRpc.call(
                         constants.ENDPOINT_API_V1_PKG,
-                        "getPkg",
+                        'getPkg',
                         [{
                             name: $routeParams.name,
                             versionType: 'NONE',
- architectureCode: undefined // not required if we don't need the version + architectureCode: undefined, // not required if we don't need the version + naturalLanguageCode: userState.naturalLanguageCode()
                         }]
                     ).then(
                     function(result) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Fri Apr 4 10:13:57 2014 UTC
@@ -66,12 +66,13 @@

                 jsonRpc.call(
                         constants.ENDPOINT_API_V1_PKG,
-                        "getPkg",
+                        'getPkg',
                         [{
                             name : $routeParams.name,
                             versionType : 'LATEST',
                             incrementViewCounter : true,
- architectureCode : $routeParams.architectureCode + architectureCode : $routeParams.architectureCode, + naturalLanguageCode: userState.naturalLanguageCode()
                         }]
                     ).then(
                     function(result) {
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Fri Apr 4 10:13:57 2014 UTC
@@ -27,6 +27,7 @@
 import org.junit.Test;

 import javax.annotation.Resource;
+import java.util.Collections;
 import java.util.List;

 public class PkgApiIT extends AbstractIntegrationTest {
@@ -117,6 +118,11 @@
         Assertions.assertThat(result.items.get(0).name).isEqualTo("pkg1");
         Assertions.assertThat(result.items.get(1).name).isEqualTo("pkg2");
     }
+
+    /**
+ * <p>In this test, an German localization is requested, but there is no localization present for German so it will
+     * fall back English.</p>
+     */

     @Test
     public void testGetPkg_found() throws ObjectNotFoundException {
@@ -126,6 +132,7 @@
         request.architectureCode = "x86";
         request.name = "pkg1";
         request.versionType = GetPkgRequest.VersionType.LATEST;
+        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;

         // ------------------------------------
         GetPkgResult result = pkgApi.getPkg(request);
@@ -137,6 +144,9 @@
         Assertions.assertThat(result.versions.get(0).major).isEqualTo("1");
         Assertions.assertThat(result.versions.get(0).micro).isEqualTo("2");
Assertions.assertThat(result.versions.get(0).revision).isEqualTo(4); + Assertions.assertThat(result.versions.get(0).naturalLanguageCode).isEqualTo(NaturalLanguage.CODE_ENGLISH); + Assertions.assertThat(result.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(result.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
     }

     @Test
@@ -147,6 +157,7 @@
         request.architectureCode = "x86";
         request.name = "pkg9";
         request.versionType = GetPkgRequest.VersionType.LATEST;
+        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;

         try {

@@ -423,12 +434,51 @@

         ObjectContext context = serverRuntime.getContext();
Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - List<PkgScreenshot> sortedScreenshotsAfter = data.pkg1.getSortedPkgScreenshots(); + List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();

         Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(3);
Assertions.assertThat(sortedScreenshotsAfter.get(0).getCode()).isEqualTo(sortedScreenshotsBefore.get(2).getCode()); Assertions.assertThat(sortedScreenshotsAfter.get(1).getCode()).isEqualTo(sortedScreenshotsBefore.get(0).getCode()); Assertions.assertThat(sortedScreenshotsAfter.get(2).getCode()).isEqualTo(sortedScreenshotsBefore.get(1).getCode());
     }
+
+ private void testUpdatePkgVersionLocalization(String naturalLanguageCode) throws Exception {
+        setAuthenticatedUserToRoot();
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+ UpdatePkgVersionLocalizationRequest request = new UpdatePkgVersionLocalizationRequest();
+        request.pkgName = data.pkg1.getName();
+        request.architectureCode = "x86";
+        request.description = "testDescription";
+        request.naturalLanguageCode = naturalLanguageCode;
+        request.summary = "testSummary";
+
+        // ------------------------------------
+        pkgApi.updatePkgVersionLocalization(request);
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
+                context,
+                pkgOptional.get(),
+ Collections.singletonList(Architecture.getByCode(context,"x86").get()));
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
+
+ Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
+    }
+
+    @Test
+ public void testUpdatePkgVersionLocalization_existingNaturalLanguage() throws Exception {
+        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_SPANISH);
+    }
+
+    @Test
+ public void testUpdatePkgVersionLocalization_newNaturalLanguage() throws Exception {
+        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_GERMAN);
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Fri Apr 4 10:13:57 2014 UTC
@@ -125,6 +125,22 @@
         result.pkg1Version2.setPkg(result.pkg1);
         result.pkg1Version2.setRepository(result.repository);

+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); + pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); + result.pkg1Version2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_SPANISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionSpanish"); + pkgVersionLocalization.setSummary("pkg1Version2SummarySpanish"); + result.pkg1Version2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
         result.pkg2 = context.newObject(Pkg.class);
         result.pkg2.setActive(true);
         result.pkg2.setName("pkg2");

==============================================================================
Revision: 2fbe296f3f5d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Apr  5 10:49:12 2014 UTC
Log: + api changes to support extraction of localization data for a pkg version

http://code.google.com/p/haiku-depot-web-app/source/detail?r=2fbe296f3f5d

Added:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgVersionLocalizationsRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgVersionLocalizationsResult.java
Modified:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java

=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgVersionLocalizationsRequest.java Sat Apr 5 10:49:12 2014 UTC
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+import java.util.List;
+
+public class GetPkgVersionLocalizationsRequest {
+
+    public String pkgName;
+
+    public List<String> naturalLanguageCodes;
+
+    public String architectureCode;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgVersionLocalizationsResult.java Sat Apr 5 10:49:12 2014 UTC
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+import java.util.List;
+
+public class GetPkgVersionLocalizationsResult {
+
+    public List<PkgVersionLocalization> pkgVersionLocalizations;
+
+    public static class PkgVersionLocalization {
+
+        public String naturalLanguageCode;
+
+        public String summary;
+
+        public String description;
+
+    }
+
+}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Sat Apr 5 10:49:12 2014 UTC
@@ -99,4 +99,11 @@

UpdatePkgVersionLocalizationResult updatePkgVersionLocalization(UpdatePkgVersionLocalizationRequest updatePkgVersionLocalizationRequest) throws ObjectNotFoundException;

+    /**
+ * <p>This method will return the package version localizations for the nominated package. It will only return + * the package version localizations for the most recent version on the package.</p>
+     */
+
+ GetPkgVersionLocalizationsResult getPkgVersionLocalizations(GetPkgVersionLocalizationsRequest getPkgVersionLocalizationsRequest) throws ObjectNotFoundException;
+
 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Sat Apr 5 10:49:12 2014 UTC
@@ -83,6 +83,32 @@

         return pkgOptional.get();
     }
+
+ private Architecture getArchitecture(ObjectContext context, String architectureCode) throws ObjectNotFoundException {
+        Preconditions.checkNotNull(context);
+        Preconditions.checkState(!Strings.isNullOrEmpty(architectureCode));
+
+ Optional<Architecture> architectureOptional = Architecture.getByCode(context,architectureCode);
+
+        if(!architectureOptional.isPresent()) {
+ throw new ObjectNotFoundException(Architecture.class.getSimpleName(), architectureCode);
+        }
+
+        return architectureOptional.get();
+    }
+
+ private NaturalLanguage getNaturalLanguage(ObjectContext context, String naturalLanguageCode) throws ObjectNotFoundException {
+        Preconditions.checkNotNull(context);
+ Preconditions.checkState(!Strings.isNullOrEmpty(naturalLanguageCode));
+
+ Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, naturalLanguageCode);
+
+        if(!naturalLanguageOptional.isPresent()) {
+ throw new ObjectNotFoundException(NaturalLanguage.class.getSimpleName(), naturalLanguageCode);
+        }
+
+        return naturalLanguageOptional.get();
+    }

     @Override
public UpdatePkgCategoriesResult updatePkgCategories(UpdatePkgCategoriesRequest updatePkgCategoriesRequest) throws ObjectNotFoundException {
@@ -321,11 +347,7 @@
architectureOptional = Architecture.getByCode(context, request.architectureCode);
         }

- Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, request.naturalLanguageCode);
-
-        if(!naturalLanguageOptional.isPresent()) {
- throw new ObjectNotFoundException(NaturalLanguage.class.getSimpleName(), request.naturalLanguageCode);
-        }
+ NaturalLanguage naturalLanguage = getNaturalLanguage(context, request.naturalLanguageCode);

         GetPkgResult result = new GetPkgResult();
         Pkg pkg = getPkg(context, request.name);
@@ -397,7 +419,7 @@

                 result.versions = Collections.singletonList(createVersion(
                         pkgVersionOptional.get(),
-                        naturalLanguageOptional.get()));
+                        naturalLanguage));

                 break;

@@ -705,29 +727,26 @@
         final ObjectContext context = serverRuntime.getContext();
Pkg pkg = getPkg(context, updatePkgVersionLocalizationRequest.pkgName);

- Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, updatePkgVersionLocalizationRequest.naturalLanguageCode);
+        User authUser = obtainAuthenticatedUser(context);

-        if(!naturalLanguageOptional.isPresent()) {
- throw new ObjectNotFoundException(NaturalLanguage.class.getSimpleName(), updatePkgVersionLocalizationRequest.naturalLanguageCode); + if(!authorizationService.check(context, authUser, pkg, Permission.PKG_EDITLOCALIZATION)) {
+            throw new AuthorizationFailureException();
         }

- Optional<Architecture> architectureOptional = Architecture.getByCode(context, updatePkgVersionLocalizationRequest.architectureCode);
-
-        if(!architectureOptional.isPresent()) {
- throw new ObjectNotFoundException(Architecture.class.getSimpleName(), updatePkgVersionLocalizationRequest.architectureCode);
-        }
+ Architecture architecture = getArchitecture(context, updatePkgVersionLocalizationRequest.architectureCode); + NaturalLanguage naturalLanguage = getNaturalLanguage(context, updatePkgVersionLocalizationRequest.naturalLanguageCode);

- Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(context, pkg, Collections.singletonList(architectureOptional.get())); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(context, pkg, Collections.singletonList(architecture));

         if(!pkgVersionOptional.isPresent()) {
- throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), pkg.getName() + "/" + architectureOptional.get().getCode()); + throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), pkg.getName() + "/" + architecture.getCode());
         }

- Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageOptional.get()); + Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguage);

         if(!pkgVersionLocalizationOptional.isPresent()) {
PkgVersionLocalization newPkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - newPkgVersionLocalization.setNaturalLanguage(naturalLanguageOptional.get());
+            newPkgVersionLocalization.setNaturalLanguage(naturalLanguage);
pkgVersionOptional.get().addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, newPkgVersionLocalization, true); pkgVersionLocalizationOptional = Optional.of(newPkgVersionLocalization);
         }
@@ -739,11 +758,46 @@

logger.info("did update the localization for pkg {} in architecture {} for natural language {}",new Object[] {
                 pkg.getName(),
-                architectureOptional.get().getCode(),
-                naturalLanguageOptional.get().getCode()
+                architecture.getCode(),
+                naturalLanguage.getCode()
         });

         return new UpdatePkgVersionLocalizationResult();
     }
+
+    @Override
+ public GetPkgVersionLocalizationsResult getPkgVersionLocalizations(GetPkgVersionLocalizationsRequest getPkgVersionLocalizationsRequest) throws ObjectNotFoundException {
+        Preconditions.checkNotNull(getPkgVersionLocalizationsRequest);
+ Preconditions.checkState(!Strings.isNullOrEmpty(getPkgVersionLocalizationsRequest.architectureCode)); + Preconditions.checkState(!Strings.isNullOrEmpty(getPkgVersionLocalizationsRequest.pkgName)); + Preconditions.checkNotNull(getPkgVersionLocalizationsRequest.naturalLanguageCodes);
+
+        final ObjectContext context = serverRuntime.getContext();
+ Pkg pkg = getPkg(context, getPkgVersionLocalizationsRequest.pkgName); + Architecture architecture = getArchitecture(context, getPkgVersionLocalizationsRequest.architectureCode);
+
+ Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(context, pkg, Collections.singletonList(architecture));
+
+        if(!pkgVersionOptional.isPresent()) {
+ throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), pkg.getName() + "/" + architecture.getCode());
+        }
+
+ GetPkgVersionLocalizationsResult result = new GetPkgVersionLocalizationsResult();
+        result.pkgVersionLocalizations = Lists.newArrayList();
+
+ for(String naturalLanguageCode : getPkgVersionLocalizationsRequest.naturalLanguageCodes) { + Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
+
+            if(pkgVersionLocalizationOptional.isPresent()) {
+ GetPkgVersionLocalizationsResult.PkgVersionLocalization resultPkgVersionLocalization = new GetPkgVersionLocalizationsResult.PkgVersionLocalization(); + resultPkgVersionLocalization.naturalLanguageCode = naturalLanguageCode; + resultPkgVersionLocalization.description = pkgVersionLocalizationOptional.get().getDescription(); + resultPkgVersionLocalization.summary = pkgVersionLocalizationOptional.get().getSummary(); + result.pkgVersionLocalizations.add(resultPkgVersionLocalization);
+            }
+        }
+
+        return result;
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Sat Apr 5 10:49:12 2014 UTC
@@ -151,6 +151,7 @@
             case PKG_EDITICON:
             case PKG_EDITSCREENSHOT:
             case PKG_EDITCATEGORIES:
+            case PKG_EDITLOCALIZATION:
return null!=authenticatedUser && authenticatedUser.getIsRoot();

             default:
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Sat Apr 5 10:49:12 2014 UTC
@@ -21,7 +21,8 @@

     PKG_EDITICON(TargetType.PKG),
     PKG_EDITSCREENSHOT(TargetType.PKG),
-    PKG_EDITCATEGORIES(TargetType.PKG);
+    PKG_EDITCATEGORIES(TargetType.PKG),
+    PKG_EDITLOCALIZATION(TargetType.PKG);

     private TargetType requiredTargetType;

=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Sat Apr 5 10:49:12 2014 UTC
@@ -100,7 +100,7 @@

     @Test
     public void searchPkgsTest() {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();

         SearchPkgsRequest request = new SearchPkgsRequest();
         request.architectureCode = "x86";
@@ -126,7 +126,7 @@

     @Test
     public void testGetPkg_found() throws ObjectNotFoundException {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();

         GetPkgRequest request = new GetPkgRequest();
         request.architectureCode = "x86";
@@ -151,7 +151,7 @@

     @Test
     public void testGetPkg_notFound() {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();

         GetPkgRequest request = new GetPkgRequest();
         request.architectureCode = "x86";
@@ -162,7 +162,7 @@
         try {

             // ------------------------------------
-            GetPkgResult result = pkgApi.getPkg(request);
+            pkgApi.getPkg(request);
             // ------------------------------------

Assert.fail("expected an instance of "+ObjectNotFoundException.class.getSimpleName()+" to be thrown, but was not");
@@ -179,7 +179,7 @@
     @Test
     public void testGetPkgIcons() throws Exception {

- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();

         // ------------------------------------
GetPkgIconsResult result = pkgApi.getPkgIcons(new GetPkgIconsRequest("pkg1"));
@@ -198,9 +198,8 @@
     public void testConfigurePkgIcon_badData() throws Exception {

         setAuthenticatedUserToRoot();
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        byte[] sample16 = getResourceData("/sample-16x16.png");
-        byte[] sample32 = getResourceData("/sample-32x32.png");
+        integrationTestSupportService.createStandardTestData();
+
         byte[] sampleHvif = getResourceData("/sample.hvif");

         ConfigurePkgIconRequest request = new ConfigurePkgIconRequest();
@@ -247,7 +246,8 @@
     public void testConfigurePkgIcon_ok() throws Exception {

         setAuthenticatedUserToRoot();
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();
+
         byte[] sample16 = getResourceData("/sample-16x16.png");
         byte[] sample32 = getResourceData("/sample-32x32.png");
         byte[] sampleHvif = getResourceData("/sample.hvif");
@@ -309,7 +309,7 @@

         setAuthenticatedUserToRoot();

- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        integrationTestSupportService.createStandardTestData();

         {
             ObjectContext objectContext = serverRuntime.getContext();
@@ -395,7 +395,7 @@

         ObjectContext context = serverRuntime.getContext();
Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - List<PkgScreenshot> sortedScreenshotsAfter = data.pkg1.getSortedPkgScreenshots(); + List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();

Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(sortedScreenshotsBefore.size()-1);

@@ -480,5 +480,30 @@
public void testUpdatePkgVersionLocalization_newNaturalLanguage() throws Exception {
         testUpdatePkgVersionLocalization(NaturalLanguage.CODE_GERMAN);
     }
+
+    /**
+ * <p>This test requests german and english, but only english ispresent so needs to check that the output
+     * contains only the english data.</p>
+     */
+
+    @Test
+    public void testGetPkgVersionLocalizations() throws Exception {
+        setAuthenticatedUserToRoot();
+
+        integrationTestSupportService.createStandardTestData();
+
+ GetPkgVersionLocalizationsRequest request = new GetPkgVersionLocalizationsRequest();
+        request.architectureCode = "x86";
+ request.naturalLanguageCodes = ImmutableList.of(NaturalLanguage.CODE_ENGLISH, NaturalLanguage.CODE_GERMAN);
+        request.pkgName = "pkg1";
+
+        // ------------------------------------
+ GetPkgVersionLocalizationsResult result = pkgApi.getPkgVersionLocalizations(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.pkgVersionLocalizations.size()).isEqualTo(1); + Assertions.assertThat(result.pkgVersionLocalizations.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(result.pkgVersionLocalizations.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
+    }

 }

==============================================================================
Revision: 881e18f28a12
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Apr  7 11:46:03 2014 UTC
Log: + changes to support sensible up-take of natural language localization into new data.

http://code.google.com/p/haiku-depot-web-app/source/detail?r=881e18f28a12

Added:
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/VersionCoordinates.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/AbstractIntegrationTest.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/PkgOrchestrationServiceIT.java
Deleted:
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/AbstractIntegrationTest.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java
Modified:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationRequest.java
 /haikudepotserver-docs/src/main/latex/docs/part-api.tex
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Architecture.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java
 /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/CaptchaApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/MiscelaneousApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/RepositoryApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/UserApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/repository/RepositoryImportServiceIT.java
 /haikudepotserver-webapp/src/test/resources/spring/test.xml

=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/VersionCoordinates.java Mon Apr 7 11:46:03 2014 UTC
@@ -0,0 +1,83 @@
+package org.haikuos.haikudepotserver.support;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+public class VersionCoordinates {
+
+    private String major;
+    private String minor;
+    private String micro;
+    private String preRelease;
+    private Integer revision;
+
+ public VersionCoordinates(String major, String minor, String micro, String preRelease, Integer revision) {
+        Preconditions.checkState(!Strings.isNullOrEmpty(major));
+        this.major = major;
+        this.minor = minor;
+        this.micro = micro;
+        this.preRelease = preRelease;
+        this.revision = revision;
+    }
+
+    public String getMajor() {
+        return major;
+    }
+
+    public String getMinor() {
+        return minor;
+    }
+
+    public String getMicro() {
+        return micro;
+    }
+
+    public String getPreRelease() {
+        return preRelease;
+    }
+
+    public Integer getRevision() {
+        return revision;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        VersionCoordinates that = (VersionCoordinates) o;
+
+        if (!major.equals(that.major)) return false;
+ if (micro != null ? !micro.equals(that.micro) : that.micro != null) return false; + if (minor != null ? !minor.equals(that.minor) : that.minor != null) return false; + if (preRelease != null ? !preRelease.equals(that.preRelease) : that.preRelease != null) return false; + if (revision != null ? !revision.equals(that.revision) : that.revision != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = major.hashCode();
+        result = 31 * result + (minor != null ? minor.hashCode() : 0);
+        result = 31 * result + (micro != null ? micro.hashCode() : 0);
+ result = 31 * result + (preRelease != null ? preRelease.hashCode() : 0); + result = 31 * result + (revision != null ? revision.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(getMajor());
+        stringBuilder.append('.');
+        stringBuilder.append(getMinor());
+        stringBuilder.append('.');
+        stringBuilder.append(getMicro());
+        stringBuilder.append('.');
+        stringBuilder.append(getPreRelease());
+        stringBuilder.append('.');
+        stringBuilder.append(getRevision());
+        return stringBuilder.toString();
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/AbstractIntegrationTest.java Mon Apr 7 11:46:03 2014 UTC
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotsever;
+
+import com.google.common.base.Optional;
+import com.google.common.io.ByteStreams;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.AuthenticationHelper;
+import org.haikuos.haikudepotserver.security.AuthenticationService;
+import org.haikuos.haikudepotserver.support.Closeables;
+import org.haikuos.haikudepotserver.support.db.migration.ManagedDatabase;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * <p>This superclass of all of the tests has a hook to run before each integration test. The hook will + * basically delete all of the schema objects and then prompt the database schema migration to again, + * repopulate the database afresh. This ensures that the database is taken from a blank state at the + * start of each test. This is important for tests to maintain their independence. The use a + * 'transaction' over the test is not possible here as the ORM technology isnot bound to a single
+ * transaction.</p>
+ */
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration({
+        "classpath:/spring/servlet-context.xml",
+        "classpath:/spring/application-context.xml",
+        "classpath:/spring/test.xml"
+})
+public abstract class AbstractIntegrationTest {
+
+ protected static Logger logger = LoggerFactory.getLogger(AbstractIntegrationTest.class);
+
+ private final static String DATABASEPRODUCTNAME_POSTGRES = "PostgreSQL";
+
+    @Resource
+    ApplicationContext applicationContext;
+
+    @Resource
+    protected ServerRuntime serverRuntime;
+
+    @Resource
+    AuthenticationService authenticationService;
+
+    protected byte[] getResourceData(String path) throws IOException {
+        InputStream inputStream = null;
+
+        try {
+            inputStream = this.getClass().getResourceAsStream(path);
+
+            if(null==inputStream) {
+ throw new IllegalStateException("unable to find the test resource; "+path);
+            }
+
+            return ByteStreams.toByteArray(inputStream);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+    /**
+ * <p>Before each test is run, we want to remove all of the database objects and then re-populate
+     * them back again.</p>
+     */
+
+    @Before
+    public void beforeEachTest() {
+
+        logger.info("will prepare for the next test");
+
+        // reset the apache cayenne cache before we go behind its back and
+        // clear out the database for the next test.
+
+        serverRuntime.getDataDomain().getQueryCache().clear();
+        serverRuntime.getDataDomain().getSharedSnapshotCache().clear();
+        logger.info("prep; have cleared out cayenne caches");
+
+        // get all of the databases that are managed.
+
+ Map<String,ManagedDatabase> managedDatabases = applicationContext.getBeansOfType(ManagedDatabase.class);
+
+        for(ManagedDatabase managedDatabase : managedDatabases.values()) {
+
+            Connection connection = null;
+            PreparedStatement statement = null;
+
+            try {
+ connection = managedDatabase.getDataSource().getConnection();
+                connection.setAutoCommit(false);
+
+ String databaseProductName = connection.getMetaData().getDatabaseProductName();
+
+ if(!databaseProductName.equals(DATABASEPRODUCTNAME_POSTGRES)) {
+                    throw new IllegalStateException(String.format(
+ "the system is designed to be tested against %s database product, but is '%s'",
+                            DATABASEPRODUCTNAME_POSTGRES,
+                            databaseProductName));
+                }
+
+                {
+ String statementString = "DROP SCHEMA "+managedDatabase.getSchema()+" CASCADE"; + statement = connection.prepareStatement(statementString);
+                    statement.execute();
+                }
+            }
+            catch(SQLException se) {
+ throw new IllegalStateException("a database problem has arisen in preparing for an integration test",se);
+            }
+            finally {
+                Closeables.closeQuietly(statement);
+                Closeables.closeQuietly(connection);
+            }
+
+            managedDatabase.migrate();
+ logger.info("prep; did drop database objects for schema '{}' and re-create them", managedDatabase.getSchema());
+        }
+
+
+        logger.info("did prepare for the next test");
+    }
+
+    protected void setAuthenticatedUser(String nickname) {
+        ObjectContext objectContext = serverRuntime.getContext();
+ Optional<User> rootUser = User.getByNickname(objectContext, nickname); + AuthenticationHelper.setAuthenticatedUserObjectId(Optional.of(rootUser.get().getObjectId()));
+    }
+
+    /**
+ * <p>Some tests will enforce authorization; in order to test the method's actual logic rather than the + * authorization logic, it is sometimes handy to force the authenticated user to be root. This method
+     * will do this.</p>
+     */
+
+    protected void setAuthenticatedUserToRoot() {
+        setAuthenticatedUser("root");
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java Mon Apr 7 11:46:03 2014 UTC
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotsever;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.MediaType;
+import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
+import org.haikuos.haikudepotserver.support.Closeables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.InputStream;
+
+/**
+ * <p>This class is designed to help out with creating some common test data that can be re-used between tests.</p>
+ */
+
+@Service
+public class IntegrationTestSupportService {
+
+ protected static Logger logger = LoggerFactory.getLogger(IntegrationTestSupportService.class);
+
+    @Resource
+    ServerRuntime serverRuntime;
+
+    @Resource
+    PkgOrchestrationService pkgService;
+
+    private ObjectContext objectContext = null;
+
+    public ObjectContext getObjectContext() {
+        if(null==objectContext) {
+            objectContext = serverRuntime.getContext();
+        }
+
+        return objectContext;
+    }
+
+ private PkgScreenshot addPkgScreenshot(ObjectContext objectContext, Pkg pkg) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = IntegrationTestSupportService.class.getResourceAsStream("/sample-320x240.png"); + return pkgService.storePkgScreenshotImage(inputStream, objectContext, pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading a sample screenshot into a test package",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+ private void addPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = this.getClass().getResourceAsStream(String.format("/sample-%dx%d.png",size,size));
+            pkgService.storePkgIconImage(
+                    inputStream,
+ MediaType.getByCode(objectContext, com.google.common.net.MediaType.PNG.toString()).get(),
+                    size,
+                    objectContext,
+                    pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading an icon",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+    private void addPkgIcons(ObjectContext objectContext, Pkg pkg) {
+        addPkgIcon(objectContext, pkg, 16);
+        addPkgIcon(objectContext, pkg, 32);
+    }
+
+    public StandardTestData createStandardTestData() {
+
+        logger.info("will create standard test data");
+
+        ObjectContext context = getObjectContext();
+        StandardTestData result = new StandardTestData();
+
+        Architecture x86 = Architecture.getByCode(context, "x86").get();
+ Architecture x86_gcc2 = Architecture.getByCode(context, "x86_gcc2").get();
+
+        result.repository = context.newObject(Repository.class);
+        result.repository.setActive(Boolean.TRUE);
+        result.repository.setCode("testrepository");
+        result.repository.setArchitecture(x86);
+        result.repository.setUrl("file:///");
+
+        result.pkg1 = context.newObject(Pkg.class);
+        result.pkg1.setActive(true);
+        result.pkg1.setName("pkg1");
+
+        addPkgScreenshot(context,result.pkg1);
+        addPkgScreenshot(context,result.pkg1);
+        addPkgScreenshot(context,result.pkg1);
+        addPkgIcons(context, result.pkg1);
+
+        result.pkg1Version1 = context.newObject(PkgVersion.class);
+        result.pkg1Version1.setActive(Boolean.FALSE);
+        result.pkg1Version1.setArchitecture(x86);
+        result.pkg1Version1.setMajor("1");
+        result.pkg1Version1.setMicro("2");
+        result.pkg1Version1.setRevision(3);
+        result.pkg1Version1.setPkg(result.pkg1);
+        result.pkg1Version1.setRepository(result.repository);
+
+        result.pkg1Version2x86 = context.newObject(PkgVersion.class);
+        result.pkg1Version2x86.setActive(Boolean.TRUE);
+        result.pkg1Version2x86.setArchitecture(x86);
+        result.pkg1Version2x86.setMajor("1");
+        result.pkg1Version2x86.setMicro("2");
+        result.pkg1Version2x86.setRevision(4);
+        result.pkg1Version2x86.setPkg(result.pkg1);
+        result.pkg1Version2x86.setRepository(result.repository);
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); + pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); + result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_SPANISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionSpanish"); + pkgVersionLocalization.setSummary("pkg1Version2SummarySpanish"); + result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        result.pkg1Version2x86_gcc2 = context.newObject(PkgVersion.class);
+        result.pkg1Version2x86_gcc2.setActive(Boolean.TRUE);
+        result.pkg1Version2x86_gcc2.setArchitecture(x86_gcc2);
+        result.pkg1Version2x86_gcc2.setMajor("1");
+        result.pkg1Version2x86_gcc2.setMicro("2");
+        result.pkg1Version2x86_gcc2.setRevision(4);
+        result.pkg1Version2x86_gcc2.setPkg(result.pkg1);
+        result.pkg1Version2x86_gcc2.setRepository(result.repository);
+
+ // this is the same as the x86 version so that comparisons with English will happen.
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); + pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); + result.pkg1Version2x86_gcc2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        result.pkg2 = context.newObject(Pkg.class);
+        result.pkg2.setActive(true);
+        result.pkg2.setName("pkg2");
+
+        result.pkg2Version1 = context.newObject(PkgVersion.class);
+        result.pkg2Version1.setActive(Boolean.TRUE);
+        result.pkg2Version1.setArchitecture(x86);
+        result.pkg2Version1.setMajor("1");
+        result.pkg2Version1.setMicro("2");
+        result.pkg2Version1.setRevision(3);
+        result.pkg2Version1.setPkg(result.pkg2);
+        result.pkg2Version1.setRepository(result.repository);
+
+        result.pkg3 = context.newObject(Pkg.class);
+        result.pkg3.setActive(true);
+        result.pkg3.setName("pkg3");
+
+        result.pkg3Version1 = context.newObject(PkgVersion.class);
+        result.pkg3Version1.setActive(Boolean.TRUE);
+        result.pkg3Version1.setArchitecture(x86);
+        result.pkg3Version1.setMajor("1");
+        result.pkg3Version1.setMicro("2");
+        result.pkg3Version1.setRevision(3);
+        result.pkg3Version1.setPkg(result.pkg3);
+        result.pkg3Version1.setRepository(result.repository);
+
+        context.commitChanges();
+
+        logger.info("did create standard test data");
+
+        return result;
+    }
+
+    /**
+ * <p>This class is a container that carries some basic test-case data.</p>
+     */
+
+    public static class StandardTestData {
+
+        public Repository repository;
+
+        public Pkg pkg1;
+        public PkgVersion pkg1Version1;
+        public PkgVersion pkg1Version2x86;
+        public PkgVersion pkg1Version2x86_gcc2;
+
+        public Pkg pkg2;
+        public PkgVersion pkg2Version1;
+
+        public Pkg pkg3;
+        public PkgVersion pkg3Version1;
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/PkgOrchestrationServiceIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -0,0 +1,114 @@
+package org.haikuos.haikudepotsever.pkg;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ObjectId;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.dataobjects.Architecture;
+import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
+import org.haikuos.haikudepotserver.dataobjects.PkgVersionLocalization;
+import org.haikuos.haikudepotserver.dataobjects.Repository;
+import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
+import org.haikuos.haikudepotserver.pkg.model.PkgRepositoryImportJob;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
+import org.haikuos.pkg.model.Pkg;
+import org.haikuos.pkg.model.PkgArchitecture;
+import org.haikuos.pkg.model.PkgVersion;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+
+public class PkgOrchestrationServiceIT extends AbstractIntegrationTest {
+
+    @Resource
+    PkgOrchestrationService pkgOrchestrationService;
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    private void importTestPackageWithMinor(String minor) {
+        ObjectContext context = serverRuntime.getContext();
+ Repository repository = Repository.getByCode(context, "testrepository").get();
+
+        org.haikuos.pkg.model.Pkg pkg = new Pkg();
+        pkg.setArchitecture(PkgArchitecture.X86);
+        pkg.setDescription("test-description-en");
+        pkg.setSummary("test-summary-en");
+        pkg.setName("testpkg");
+        pkg.setVersion(new PkgVersion("1", minor, "3", "4", 5));
+
+        pkgOrchestrationService.importFrom(
+                context,
+                repository.getObjectId(),
+                pkg);
+
+        context.commitChanges();
+    }
+
+    /**
+ * <p>Given a series of imported packages, the system will try to replicate any localizations sensibly. This test
+     * will check to make sure this works.</p>
+     */
+
+    @Test
+    public void testImportFrom_replicateLocalizationIfEnglishMatches() {
+
+        // create the repository
+
+        integrationTestSupportService.createStandardTestData();
+
+        // import the first version
+
+        importTestPackageWithMinor("2");
+
+        // now configure another language.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); + org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
+                    context,
+                    pkg,
+                    Collections.singletonList(
+                            Architecture.getByCode(context, "x86").get()
+                    )).get();
+
+            pkgOrchestrationService.updatePkgVersionLocalization(
+                            context,
+                            pkgVersion,
+ NaturalLanguage.getByCode(context, NaturalLanguage.CODE_GERMAN).get(),
+                            "test-summary-de",
+                            "test-description-de");
+
+            context.commitChanges();
+        }
+
+        // now load the next package version in
+
+        importTestPackageWithMinor("3");
+
+        // the second package should have the same german localization
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); + org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
+                    context,
+                    pkg,
+                    Collections.singletonList(
+                            Architecture.getByCode(context, "x86").get()
+                    )).get();
+
+            Assertions.assertThat(pkgVersion.getMajor()).isEqualTo("1");
+            Assertions.assertThat(pkgVersion.getMinor()).isEqualTo("3");
+
+ PkgVersionLocalization localization = pkgVersion.getPkgVersionLocalization(NaturalLanguage.CODE_GERMAN).get();
+
+ Assertions.assertThat(localization.getSummary()).isEqualTo("test-summary-de"); + Assertions.assertThat(localization.getDescription()).isEqualTo("test-description-de");
+        }
+
+    }
+
+}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/AbstractIntegrationTest.java Fri Mar 28 10:40:14 2014 UTC
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1.support;
-
-import com.google.common.base.Optional;
-import com.google.common.io.ByteStreams;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.haikuos.haikudepotserver.dataobjects.User;
-import org.haikuos.haikudepotserver.security.AuthenticationHelper;
-import org.haikuos.haikudepotserver.security.AuthenticationService;
-import org.haikuos.haikudepotserver.support.Closeables;
-import org.haikuos.haikudepotserver.support.db.migration.ManagedDatabase;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.Map;
-
-/**
- * <p>This superclass of all of the tests has a hook to run before each integration test. The hook will - * basically delete all of the schema objects and then prompt the database schema migration to again, - * repopulate the database afresh. This ensures that the database is taken from a blank state at the - * start of each test. This is important for tests to maintain their independence. The use a - * 'transaction' over the test is not possible here as the ORM technology isnot bound to a single
- * transaction.</p>
- */
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration({
-        "classpath:/spring/servlet-context.xml",
-        "classpath:/spring/application-context.xml",
-        "classpath:/spring/test.xml"
-})
-public abstract class AbstractIntegrationTest {
-
- protected static Logger logger = LoggerFactory.getLogger(AbstractIntegrationTest.class);
-
- private final static String DATABASEPRODUCTNAME_POSTGRES = "PostgreSQL";
-
-    @Resource
-    ApplicationContext applicationContext;
-
-    @Resource
-    protected ServerRuntime serverRuntime;
-
-    @Resource
-    AuthenticationService authenticationService;
-
-    protected byte[] getResourceData(String path) throws IOException {
-        InputStream inputStream = null;
-
-        try {
-            inputStream = this.getClass().getResourceAsStream(path);
-
-            if(null==inputStream) {
- throw new IllegalStateException("unable to find the test resource; "+path);
-            }
-
-            return ByteStreams.toByteArray(inputStream);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
-    /**
- * <p>Before each test is run, we want to remove all of the database objects and then re-populate
-     * them back again.</p>
-     */
-
-    @Before
-    public void beforeEachTest() {
-
-        logger.info("will prepare for the next test");
-
-        // reset the apache cayenne cache before we go behind its back and
-        // clear out the database for the next test.
-
-        serverRuntime.getDataDomain().getQueryCache().clear();
-        serverRuntime.getDataDomain().getSharedSnapshotCache().clear();
-        logger.info("prep; have cleared out cayenne caches");
-
-        // get all of the databases that are managed.
-
- Map<String,ManagedDatabase> managedDatabases = applicationContext.getBeansOfType(ManagedDatabase.class);
-
-        for(ManagedDatabase managedDatabase : managedDatabases.values()) {
-
-            Connection connection = null;
-            PreparedStatement statement = null;
-
-            try {
- connection = managedDatabase.getDataSource().getConnection();
-                connection.setAutoCommit(false);
-
- String databaseProductName = connection.getMetaData().getDatabaseProductName();
-
- if(!databaseProductName.equals(DATABASEPRODUCTNAME_POSTGRES)) {
-                    throw new IllegalStateException(String.format(
- "the system is designed to be tested against %s database product, but is '%s'",
-                            DATABASEPRODUCTNAME_POSTGRES,
-                            databaseProductName));
-                }
-
-                {
- String statementString = "DROP SCHEMA "+managedDatabase.getSchema()+" CASCADE"; - statement = connection.prepareStatement(statementString);
-                    statement.execute();
-                }
-            }
-            catch(SQLException se) {
- throw new IllegalStateException("a database problem has arisen in preparing for an integration test",se);
-            }
-            finally {
-                Closeables.closeQuietly(statement);
-                Closeables.closeQuietly(connection);
-            }
-
-            managedDatabase.migrate();
- logger.info("prep; did drop database objects for schema '{}' and re-create them", managedDatabase.getSchema());
-        }
-
-
-        logger.info("did prepare for the next test");
-    }
-
-    protected void setAuthenticatedUser(String nickname) {
-        ObjectContext objectContext = serverRuntime.getContext();
- Optional<User> rootUser = User.getByNickname(objectContext, nickname); - AuthenticationHelper.setAuthenticatedUserObjectId(Optional.of(rootUser.get().getObjectId()));
-    }
-
-    /**
- * <p>Some tests will enforce authorization; in order to test the method's actual logic rather than the - * authorization logic, it is sometimes handy to force the authenticated user to be root. This method
-     * will do this.</p>
-     */
-
-    protected void setAuthenticatedUserToRoot() {
-        setAuthenticatedUser("root");
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/support/IntegrationTestSupportService.java Fri Apr 4 10:13:57 2014 UTC
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1.support;
-
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.haikuos.haikudepotserver.dataobjects.*;
-import org.haikuos.haikudepotserver.dataobjects.MediaType;
-import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
-import org.haikuos.haikudepotserver.support.Closeables;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.io.InputStream;
-
-/**
- * <p>This class is designed to help out with creating some common test data that can be re-used between tests.</p>
- */
-
-@Service
-public class IntegrationTestSupportService {
-
- protected static Logger logger = LoggerFactory.getLogger(IntegrationTestSupportService.class);
-
-    @Resource
-    ServerRuntime serverRuntime;
-
-    @Resource
-    PkgOrchestrationService pkgService;
-
-    private ObjectContext objectContext = null;
-
-    public ObjectContext getObjectContext() {
-        if(null==objectContext) {
-            objectContext = serverRuntime.getContext();
-        }
-
-        return objectContext;
-    }
-
- private PkgScreenshot addPkgScreenshot(ObjectContext objectContext, Pkg pkg) {
-        InputStream inputStream = null;
-
-        try {
- inputStream = IntegrationTestSupportService.class.getResourceAsStream("/sample-320x240.png"); - return pkgService.storePkgScreenshotImage(inputStream, objectContext, pkg);
-        }
-        catch(Exception e) {
- throw new IllegalStateException("an issue has arisen loading a sample screenshot into a test package",e);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
- private void addPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
-        InputStream inputStream = null;
-
-        try {
- inputStream = this.getClass().getResourceAsStream(String.format("/sample-%dx%d.png",size,size));
-            pkgService.storePkgIconImage(
-                    inputStream,
- MediaType.getByCode(objectContext, com.google.common.net.MediaType.PNG.toString()).get(),
-                    size,
-                    objectContext,
-                    pkg);
-        }
-        catch(Exception e) {
- throw new IllegalStateException("an issue has arisen loading an icon",e);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
-    private void addPkgIcons(ObjectContext objectContext, Pkg pkg) {
-        addPkgIcon(objectContext, pkg, 16);
-        addPkgIcon(objectContext, pkg, 32);
-    }
-
-    public StandardTestData createStandardTestData() {
-
-        logger.info("will create standard test data");
-
-        ObjectContext context = getObjectContext();
-        StandardTestData result = new StandardTestData();
-
-        Architecture x86 = Architecture.getByCode(context, "x86").get();
-
-        result.repository = context.newObject(Repository.class);
-        result.repository.setActive(Boolean.TRUE);
-        result.repository.setCode("testrepository");
-        result.repository.setArchitecture(x86);
-        result.repository.setUrl("file:///");
-
-        result.pkg1 = context.newObject(Pkg.class);
-        result.pkg1.setActive(true);
-        result.pkg1.setName("pkg1");
-
-        addPkgScreenshot(context,result.pkg1);
-        addPkgScreenshot(context,result.pkg1);
-        addPkgScreenshot(context,result.pkg1);
-        addPkgIcons(context, result.pkg1);
-
-        result.pkg1Version1 = context.newObject(PkgVersion.class);
-        result.pkg1Version1.setActive(Boolean.FALSE);
-        result.pkg1Version1.setArchitecture(x86);
-        result.pkg1Version1.setMajor("1");
-        result.pkg1Version1.setMicro("2");
-        result.pkg1Version1.setRevision(3);
-        result.pkg1Version1.setPkg(result.pkg1);
-        result.pkg1Version1.setRepository(result.repository);
-
-        result.pkg1Version2 = context.newObject(PkgVersion.class);
-        result.pkg1Version2.setActive(Boolean.TRUE);
-        result.pkg1Version2.setArchitecture(x86);
-        result.pkg1Version2.setMajor("1");
-        result.pkg1Version2.setMicro("2");
-        result.pkg1Version2.setRevision(4);
-        result.pkg1Version2.setPkg(result.pkg1);
-        result.pkg1Version2.setRepository(result.repository);
-
-        {
- PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); - pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); - pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); - result.pkg1Version2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
-        }
-
-        {
- PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_SPANISH).get()); - pkgVersionLocalization.setDescription("pkg1Version2DescriptionSpanish"); - pkgVersionLocalization.setSummary("pkg1Version2SummarySpanish"); - result.pkg1Version2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
-        }
-
-        result.pkg2 = context.newObject(Pkg.class);
-        result.pkg2.setActive(true);
-        result.pkg2.setName("pkg2");
-
-        result.pkg2Version1 = context.newObject(PkgVersion.class);
-        result.pkg2Version1.setActive(Boolean.TRUE);
-        result.pkg2Version1.setArchitecture(x86);
-        result.pkg2Version1.setMajor("1");
-        result.pkg2Version1.setMicro("2");
-        result.pkg2Version1.setRevision(3);
-        result.pkg2Version1.setPkg(result.pkg2);
-        result.pkg2Version1.setRepository(result.repository);
-
-        result.pkg3 = context.newObject(Pkg.class);
-        result.pkg3.setActive(true);
-        result.pkg3.setName("pkg3");
-
-        result.pkg3Version1 = context.newObject(PkgVersion.class);
-        result.pkg3Version1.setActive(Boolean.TRUE);
-        result.pkg3Version1.setArchitecture(x86);
-        result.pkg3Version1.setMajor("1");
-        result.pkg3Version1.setMicro("2");
-        result.pkg3Version1.setRevision(3);
-        result.pkg3Version1.setPkg(result.pkg3);
-        result.pkg3Version1.setRepository(result.repository);
-
-        context.commitChanges();
-
-        logger.info("did create standard test data");
-
-        return result;
-    }
-
-    /**
- * <p>This class is a container that carries some basic test-case data.</p>
-     */
-
-    public static class StandardTestData {
-
-        public Repository repository;
-
-        public Pkg pkg1;
-        public PkgVersion pkg1Version1;
-        public PkgVersion pkg1Version2;
-
-        public Pkg pkg2;
-        public PkgVersion pkg2Version1;
-
-        public Pkg pkg3;
-        public PkgVersion pkg3Version1;
-    }
-
-}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationRequest.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/UpdatePkgVersionLocalizationRequest.java Mon Apr 7 11:46:03 2014 UTC
@@ -13,6 +13,15 @@

     public String architectureCode;

+    /**
+ * <p>Localizations may differ between architectures because the description etc... may be different. More often + * than not though, the localizations will be the same. For this reason this flag is provided. When set to true, + * if the english variant that is being considered is the same as other architectures, then the localization will + * also be replicated into the latest package for those architectures as well.</p>
+     */
+
+    public Boolean replicateToOtherArchitecturesWithSameEnglishContent;
+
     public String description;

     public String summary;
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-api.tex Mon Feb 24 10:23:41 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-api.tex Mon Apr 7 11:46:03 2014 UTC
@@ -85,6 +85,8 @@
  }]
 }
 \end{verbatim}
+
+The ``id'' value here can be anything. It must be supplied and is used to correlate a request with a response; the same ``id'' value that arrives in the request is relayed back in the response. If a client were using the API in an asynchronous manner then being able to correlate the request and the response is very important.

All going well, the following (abridged) form of response would be sent back to the client;

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Sat Apr 5 10:49:12 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Mon Apr 7 11:46:03 2014 UTC
@@ -723,6 +723,7 @@
Preconditions.checkState(!updatePkgVersionLocalizationRequest.naturalLanguageCode.equals(NaturalLanguage.CODE_ENGLISH)); Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.pkgName)); Preconditions.checkState(!Strings.isNullOrEmpty(updatePkgVersionLocalizationRequest.summary)); + Preconditions.checkNotNull(updatePkgVersionLocalizationRequest.replicateToOtherArchitecturesWithSameEnglishContent);

         final ObjectContext context = serverRuntime.getContext();
Pkg pkg = getPkg(context, updatePkgVersionLocalizationRequest.pkgName);
@@ -742,17 +743,38 @@
throw new ObjectNotFoundException(PkgVersion.class.getSimpleName(), pkg.getName() + "/" + architecture.getCode());
         }

- Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguage);
+        pkgService.updatePkgVersionLocalization(
+                context,
+                pkgVersionOptional.get(),
+                naturalLanguage,
+                updatePkgVersionLocalizationRequest.summary,
+                updatePkgVersionLocalizationRequest.description);

-        if(!pkgVersionLocalizationOptional.isPresent()) {
- PkgVersionLocalization newPkgVersionLocalization = context.newObject(PkgVersionLocalization.class);
-            newPkgVersionLocalization.setNaturalLanguage(naturalLanguage);
- pkgVersionOptional.get().addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, newPkgVersionLocalization, true); - pkgVersionLocalizationOptional = Optional.of(newPkgVersionLocalization);
-        }
+ if(updatePkgVersionLocalizationRequest.replicateToOtherArchitecturesWithSameEnglishContent) {

- pkgVersionLocalizationOptional.get().setSummary(updatePkgVersionLocalizationRequest.summary); - pkgVersionLocalizationOptional.get().setDescription(updatePkgVersionLocalizationRequest.description); + for(Architecture architectureToCopyTo : Architecture.getAll(context)) {
+
+ if(!architectureToCopyTo.equals(architecture)) { // don't copy the source to the destination.
+
+ Optional<PkgVersion> pkgVersionOptionalToCopyTo = PkgVersion.getForPkg(
+                            context,
+                            pkg,
+                            architectureToCopyTo,
+ pkgVersionOptional.get().toVersionCoordinates());
+
+                    if(pkgVersionOptionalToCopyTo.isPresent()) {
+                        pkgService.replicateLocalizationIfEnglishMatches(
+                                context,
+                                pkgVersionOptional.get(),
+                                pkgVersionOptionalToCopyTo.get(),
+                                Collections.singletonList(naturalLanguage),
+ true // override any destination localization already present
+                        );
+                    }
+                }
+            }
+
+        }

         context.commitChanges();

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Architecture.java Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Architecture.java Mon Apr 7 11:46:03 2014 UTC
@@ -37,5 +37,10 @@
ExpressionFactory.matchExp(Architecture.CODE_PROPERTY, code))),
                 null));
     }
+
+    @Override
+    public String toString() {
+        return "arch;"+getCode();
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Mon Apr 7 11:46:03 2014 UTC
@@ -35,6 +35,15 @@
query.addOrdering(new Ordering(NAME_PROPERTY, SortOrder.ASCENDING));
         return (List<NaturalLanguage>) context.performQuery(query);
     }
+
+ public static List<NaturalLanguage> getAllExceptEnglish(ObjectContext context) {
+        Preconditions.checkNotNull(context);
+        SelectQuery query = new SelectQuery(
+                NaturalLanguage.class,
+ ExpressionFactory.noMatchExp(NaturalLanguage.CODE_PROPERTY, CODE_ENGLISH)); + query.addOrdering(new Ordering(NAME_PROPERTY, SortOrder.ASCENDING));
+        return (List<NaturalLanguage>) context.performQuery(query);
+    }

public static Optional<NaturalLanguage> getByCode(ObjectContext context, String code) {
         Preconditions.checkNotNull(context);
@@ -45,5 +54,10 @@
ExpressionFactory.matchExp(MediaType.CODE_PROPERTY, code))),
                 null));
     }
+
+    @Override
+    public String toString() {
+        return "nat-lang;"+getCode();
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Mon Apr 7 11:46:03 2014 UTC
@@ -21,6 +21,8 @@
 import org.apache.cayenne.validation.ValidationResult;
 import org.haikuos.haikudepotserver.dataobjects.auto._PkgVersion;
import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;
+import org.haikuos.haikudepotserver.support.VersionCoordinates;
+import sun.tools.tree.AndExpression;

 import java.util.List;
 import java.util.regex.Pattern;
@@ -32,15 +34,45 @@
public final static Pattern MICRO_PATTERN = Pattern.compile("^[\\w_.]+$"); public final static Pattern PRE_RELEASE_PATTERN = Pattern.compile("^[\\w_.]+$");

+ // TODO; could there be a problem here with alpha ordering of version numbers???
     public static List<Ordering> versionOrdering() {
         List<Ordering> result = Lists.newArrayList();
result.add(new Ordering(PkgVersion.MAJOR_PROPERTY, SortOrder.DESCENDING_INSENSITIVE)); result.add(new Ordering(PkgVersion.MINOR_PROPERTY, SortOrder.DESCENDING_INSENSITIVE)); result.add(new Ordering(PkgVersion.MICRO_PROPERTY, SortOrder.DESCENDING_INSENSITIVE)); result.add(new Ordering(PkgVersion.PRE_RELEASE_PROPERTY, SortOrder.DESCENDING_INSENSITIVE)); - result.add(new Ordering(PkgVersion.REVISION_PROPERTY, SortOrder.DESCENDING_INSENSITIVE)); + result.add(new Ordering(PkgVersion.REVISION_PROPERTY, SortOrder.DESCENDING));
         return result;
     }
+
+    public static Optional<PkgVersion> getForPkg(
+            ObjectContext context,
+            Pkg pkg,
+            Architecture architecture,
+            VersionCoordinates versionCoordinates) {
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(architecture);
+        Preconditions.checkNotNull(versionCoordinates);
+
+        SelectQuery query = new SelectQuery(
+                PkgVersion.class,
+ ExpressionFactory.matchExp(PkgVersion.PKG_PROPERTY, pkg).andExp( + ExpressionFactory.matchExp(PkgVersion.ACTIVE_PROPERTY, Boolean.TRUE)).andExp( + ExpressionFactory.matchExp(PkgVersion.ARCHITECTURE_PROPERTY, architecture)).andExp( + ExpressionFactory.matchExp(PkgVersion.MAJOR_PROPERTY, versionCoordinates.getMajor())).andExp( + ExpressionFactory.matchExp(PkgVersion.MINOR_PROPERTY, versionCoordinates.getMinor())).andExp( + ExpressionFactory.matchExp(PkgVersion.MICRO_PROPERTY, versionCoordinates.getMicro())).andExp( + ExpressionFactory.matchExp(PkgVersion.PRE_RELEASE_PROPERTY, versionCoordinates.getPreRelease())).andExp( + ExpressionFactory.matchExp(PkgVersion.REVISION_PROPERTY, versionCoordinates.getRevision()))
+        );
+
+        query.addOrderings(versionOrdering());
+
+        return Optional.fromNullable(Iterables.getOnlyElement(
+                (List<PkgVersion>) context.performQuery(query),
+                null));
+    }

     public static Optional<PkgVersion> getLatestForPkg(
             ObjectContext context,
@@ -69,6 +101,9 @@
ExpressionFactory.matchExp(PkgVersion.ACTIVE_PROPERTY, Boolean.TRUE)).andExp(
                         architectureExpression));

+        query.setFetchLimit(1);
+        query.addOrderings(versionOrdering());
+
         return Optional.fromNullable(Iterables.getOnlyElement(
                 (List<PkgVersion>) context.performQuery(query),
                 null));
@@ -155,5 +190,19 @@
                 }
         );
     }
+
+    public VersionCoordinates toVersionCoordinates() {
+        return new VersionCoordinates(
+                getMajor(),
+                getMinor(),
+                getMicro(),
+                getPreRelease(),
+                getRevision());
+    }
+
+    @Override
+    public String toString() {
+        return toVersionCoordinates().toString();
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java Mon Apr 7 11:46:03 2014 UTC
@@ -9,4 +9,10 @@

 public class PkgVersionLocalization extends _PkgVersionLocalization {

+    public boolean equalsForContent(PkgVersionLocalization other) {
+        return
+                getSummary().equals(other.getSummary())
+                && getDescription().equals(other.getDescription());
+    }
+
 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Mon Apr 7 11:46:03 2014 UTC
@@ -531,15 +531,20 @@

Optional<org.haikuos.haikudepotserver.dataobjects.Pkg> persistedPkgOptional = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(objectContext, pkg.getName());
         org.haikuos.haikudepotserver.dataobjects.Pkg persistedPkg;
+ Optional<org.haikuos.haikudepotserver.dataobjects.PkgVersion> persistedLatestExistingPkgVersion = Optional.absent(); + Architecture architecture = Architecture.getByCode(objectContext, pkg.getArchitecture().name().toLowerCase()).get(); org.haikuos.haikudepotserver.dataobjects.PkgVersion persistedPkgVersion = null;

         if(!persistedPkgOptional.isPresent()) {
+
persistedPkg = objectContext.newObject(org.haikuos.haikudepotserver.dataobjects.Pkg.class);
             persistedPkg.setName(pkg.getName());
             persistedPkg.setActive(Boolean.TRUE);
+
logger.info("the package {} did not exist; will create",pkg.getName());
         }
         else {
+
             persistedPkg = persistedPkgOptional.get();

// if we know that the package exists then we should look for the version.
@@ -554,6 +559,11 @@
             persistedPkgVersion = Iterables.getOnlyElement(
(List<org.haikuos.haikudepotserver.dataobjects.PkgVersion>) objectContext.performQuery(selectQuery),
                     null);
+
+            persistedLatestExistingPkgVersion = PkgVersion.getLatestForPkg(
+                    objectContext,
+                    persistedPkg,
+                    Collections.singletonList(architecture));
         }

         if(null==persistedPkgVersion) {
@@ -566,9 +576,7 @@
persistedPkgVersion.setPreRelease(pkg.getVersion().getPreRelease()); persistedPkgVersion.setRevision(pkg.getVersion().getRevision());
             persistedPkgVersion.setRepository(repository);
-            persistedPkgVersion.setArchitecture(Architecture.getByCode(
-                    objectContext,
-                    pkg.getArchitecture().name().toLowerCase()).get());
+            persistedPkgVersion.setArchitecture(architecture);
             persistedPkgVersion.setPkg(persistedPkg);

             // now add the copyrights
@@ -595,20 +603,145 @@
             }

if(!Strings.isNullOrEmpty(pkg.getSummary()) | | !Strings.isNullOrEmpty(pkg.getDescription())) { - Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(objectContext, NaturalLanguage.CODE_ENGLISH); - PkgVersionLocalization persistedPkgVersionLocalization = objectContext.newObject(PkgVersionLocalization.class); - persistedPkgVersionLocalization.setDescription(pkg.getDescription()); - persistedPkgVersionLocalization.setSummary(pkg.getSummary()); - persistedPkgVersionLocalization.setPkgVersion(persistedPkgVersion); - persistedPkgVersionLocalization.setNaturalLanguage(naturalLanguageOptional.get());
+                updatePkgVersionLocalization(
+                        objectContext,
+                        persistedPkgVersion,
+ NaturalLanguage.getByCode(objectContext, NaturalLanguage.CODE_ENGLISH).get(),
+                        pkg.getSummary(),
+                        pkg.getDescription());
             }

- logger.info("the version {} of package {} did not exist; will create", pkg.getVersion().toString(), pkg.getName()); + // look back at the previous version of the same package and see if there are localizations. If there + // are then replicate those into this version as well, but only if the english variant exists.
+
+            if(persistedLatestExistingPkgVersion.isPresent()) {
+ int naturalLanguagesReplicated = replicateLocalizationIfEnglishMatches(
+                        objectContext,
+                        persistedLatestExistingPkgVersion.get(),
+                        persistedPkgVersion,
+                        NaturalLanguage.getAllExceptEnglish(objectContext),
+                        false).size();
+
+                logger.info(
+ "replicated {} natural language localizations when creating new version of package {}",
+                        naturalLanguagesReplicated,
+                        pkg.getName());
+            }
+
+            logger.info(
+ "the version {} of package {} did not exist; will create",
+                    pkg.getVersion().toString(),
+                    pkg.getName());
         }

         logger.info("have processed package {}",pkg.toString());

     }

+    // -------------------------------------
+    // LOCALIZATION
+
+    /**
+ * <p>This method will either find the existing localization or create a new one. It will then set the localized
+     * values for the package.</p>
+     */
+
+    public PkgVersionLocalization updatePkgVersionLocalization(
+            ObjectContext context,
+            PkgVersion pkgVersion,
+            NaturalLanguage naturalLanguage,
+            String summary,
+            String description) {
+
+        Preconditions.checkNotNull(naturalLanguage);
+        Preconditions.checkState(!Strings.isNullOrEmpty(summary));
+        Preconditions.checkState(!Strings.isNullOrEmpty(description));
+
+        Optional<PkgVersionLocalization> pkgVersionLocalizationOptional =
+                pkgVersion.getPkgVersionLocalization(naturalLanguage);
+        PkgVersionLocalization pkgVersionLocalization;
+
+        if(!pkgVersionLocalizationOptional.isPresent()) {
+ pkgVersionLocalization = context.newObject(PkgVersionLocalization.class);
+            pkgVersionLocalization.setNaturalLanguage(naturalLanguage);
+ pkgVersion.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+        else {
+            pkgVersionLocalization = pkgVersionLocalizationOptional.get();
+        }
+
+        pkgVersionLocalization.setDescription(description);
+        pkgVersionLocalization.setSummary(summary);
+
+        return pkgVersionLocalization;
+    }
+
+    /**
+ * <p>This method will replicate the localization of specific natural languages if and only if the english + * language variant is the same. It will return the natural languages for which the operation was performed.</p>
+     */
+
+    public List<NaturalLanguage> replicateLocalizationIfEnglishMatches(
+            ObjectContext context,
+            PkgVersion pkgVersionSource,
+            PkgVersion pkgVersionDestination,
+            List<NaturalLanguage> naturalLanguages,
+            boolean allowOverrideDestination) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(pkgVersionSource);
+        Preconditions.checkNotNull(pkgVersionDestination);
+        Preconditions.checkNotNull(naturalLanguages);
+
+        // check that english is not in the destination languages.
+
+        for(NaturalLanguage naturalLanguage : naturalLanguages) {
+ if(naturalLanguage.getCode().equals(NaturalLanguage.CODE_ENGLISH)) { + throw new IllegalStateException("it is not possible to replicate to the english language");
+            }
+        }
+
+ Optional<PkgVersionLocalization> sourceEn = pkgVersionSource.getPkgVersionLocalization(NaturalLanguage.CODE_ENGLISH); + Optional<PkgVersionLocalization> destinationEn = pkgVersionSource.getPkgVersionLocalization(NaturalLanguage.CODE_ENGLISH);
+
+        if(
+                sourceEn.isPresent()
+                && destinationEn.isPresent()
+                && sourceEn.get().equalsForContent(destinationEn.get()) ) {
+
+ List<NaturalLanguage> naturalLanguagesEffected = Lists.newArrayList();
+
+            for(NaturalLanguage naturalLanguage : naturalLanguages) {
+
+ Optional<PkgVersionLocalization> sourceOther = pkgVersionSource.getPkgVersionLocalization(naturalLanguage.getCode());
+
+ // if there is no record of the source language then there's nothing to copy so no point in copying.
+
+                if(sourceOther.isPresent()) {
+
+ Optional<PkgVersionLocalization> destinationOther = pkgVersionDestination.getPkgVersionLocalization(naturalLanguage.getCode());
+
+ // if there is already a destination language then don't override it unless the client actually
+                    // wants to explicitly override the destination.
+
+ if(!destinationOther.isPresent() || allowOverrideDestination) {
+
+                        updatePkgVersionLocalization(
+                                context,
+                                pkgVersionDestination,
+                                naturalLanguage,
+                                sourceOther.get().getSummary(),
+                                sourceOther.get().getDescription());
+
+                        naturalLanguagesEffected.add(naturalLanguage);
+                    }
+                }
+            }
+
+            return naturalLanguagesEffected;
+        }
+
+        return Collections.emptyList();
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java Mon Apr 7 11:46:03 2014 UTC
@@ -103,11 +103,14 @@
     }

     /**
-     * <p>Returns true if the service is actively working on a job.</p>
+ * <p>Returns true if the service is actively working on a job or it has a job submitted which has not yet
+     * been dequeued and run.</p>
      */

     public boolean isProcessingSubmittedJobs() {
-        return null!=executor && executor.getActiveCount() > 0;
+        return
+                null!=executor
+ && (executor.getActiveCount() > 0 | | !executor.getQueue().isEmpty());
     }

     /**
=======================================
--- /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Sat Mar 29 10:27:45 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Mon Apr 7 11:46:03 2014 UTC
@@ -152,7 +152,7 @@
                <db-key-generator>
                        <db-generator-type>ORACLE</db-generator-type>
<db-generator-name>haikudepot.pkg_version_copyright_seq</db-generator-name>
-                       <db-key-cache-size>10</db-key-cache-size>
+                       <db-key-cache-size>1</db-key-cache-size>
                </db-key-generator>
        </db-entity>
        <db-entity name="pkg_version_license" schema="haikudepot">
@@ -162,7 +162,7 @@
                <db-key-generator>
                        <db-generator-type>ORACLE</db-generator-type>
<db-generator-name>haikudepot.pkg_version_license_seq</db-generator-name>
-                       <db-key-cache-size>10</db-key-cache-size>
+                       <db-key-cache-size>1</db-key-cache-size>
                </db-key-generator>
        </db-entity>
        <db-entity name="pkg_version_localization" schema="haikudepot">
@@ -174,7 +174,7 @@
                <db-key-generator>
                        <db-generator-type>ORACLE</db-generator-type>
<db-generator-name>haikudepot.pkg_version_localization_seq</db-generator-name>
-                       <db-key-cache-size>10</db-key-cache-size>
+                       <db-key-cache-size>1</db-key-cache-size>
                </db-key-generator>
        </db-entity>
        <db-entity name="pkg_version_url" schema="haikudepot">
@@ -185,7 +185,7 @@
                <db-key-generator>
                        <db-generator-type>ORACLE</db-generator-type>
                        
<db-generator-name>haikudepot.pkg_version_url_seq</db-generator-name>
-                       <db-key-cache-size>10</db-key-cache-size>
+                       <db-key-cache-size>1</db-key-cache-size>
                </db-key-generator>
        </db-entity>
        <db-entity name="publisher" schema="haikudepot">
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/CaptchaApiIT.java Sat Jan 18 09:59:17 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/CaptchaApiIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -10,7 +10,7 @@
import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaRequest; import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaResult;
 import org.haikuos.haikudepotserver.captcha.model.CaptchaRepository;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
 import org.junit.Test;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/MiscelaneousApiIT.java Sun Mar 23 10:09:17 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/MiscelaneousApiIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -18,8 +18,8 @@
 import org.haikuos.haikudepotserver.security.model.Permission;
 import org.haikuos.haikudepotserver.api1.model.miscellaneous.*;
 import org.haikuos.haikudepotserver.support.RuntimeInformationService;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
 import org.junit.Test;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Sat Apr 5 10:49:12 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -22,8 +22,8 @@
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
 import org.haikuos.haikudepotserver.dataobjects.*;
 import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
 import org.junit.Test;

 import javax.annotation.Resource;
@@ -453,22 +453,43 @@
         request.description = "testDescription";
         request.naturalLanguageCode = naturalLanguageCode;
         request.summary = "testSummary";
+ request.replicateToOtherArchitecturesWithSameEnglishContent = Boolean.TRUE;

         // ------------------------------------
         pkgApi.updatePkgVersionLocalization(request);
         // ------------------------------------

-        ObjectContext context = serverRuntime.getContext();
- Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
-                context,
-                pkgOptional.get(),
- Collections.singletonList(Architecture.getByCode(context,"x86").get()));
+        {
+            ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
+                    context,
+                    pkgOptional.get(),
+ Collections.singletonList(Architecture.getByCode(context, "x86").get()));
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
+
+ Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
+        }
+
+ // check that the data is copied to other architecture. A x86_gcc2 package version is known to be
+        // present in the test data.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
+                    context,
+                    pkgOptional.get(),
+ Collections.singletonList(Architecture.getByCode(context, "x86_gcc2").get()));
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);

- Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
+        }

- Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); - Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
     }

     @Test
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/RepositoryApiIT.java Sun Feb 9 06:49:48 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/RepositoryApiIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -15,8 +15,8 @@
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
 import org.haikuos.haikudepotserver.api1.support.ValidationException;
 import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
 import org.junit.Test;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/UserApiIT.java Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/UserApiIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -6,7 +6,6 @@
 package org.haikuos.haikudepotsever.api1;

 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
 import org.apache.cayenne.ObjectContext;
 import org.fest.assertions.Assertions;
 import org.haikuos.haikudepotserver.api1.UserApi;
@@ -17,7 +16,7 @@
 import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
 import org.haikuos.haikudepotserver.dataobjects.User;
 import org.haikuos.haikudepotserver.security.AuthenticationService;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
 import org.junit.Test;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java Mon Feb 24 08:20:27 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -5,16 +5,12 @@

 package org.haikuos.haikudepotsever.pkg.controller;

-import com.google.common.base.Optional;
 import com.google.common.net.MediaType;
-import org.apache.cayenne.ObjectContext;
 import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.dataobjects.Pkg;
 import org.haikuos.haikudepotserver.pkg.controller.PkgIconController;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
 import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Mon Feb 24 10:33:46 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -6,23 +6,20 @@
 package org.haikuos.haikudepotsever.pkg.controller;

 import com.google.common.base.Optional;
-import com.google.common.io.ByteStreams;
 import com.google.common.net.MediaType;
 import org.apache.cayenne.ObjectContext;
 import org.fest.assertions.Assertions;
 import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
 import org.haikuos.haikudepotserver.pkg.controller.PkgScreenshotController;
-import org.haikuos.haikudepotserver.support.Closeables;
 import org.haikuos.haikudepotserver.support.ImageHelper;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.IntegrationTestSupportService;
 import org.junit.Test;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;

 import javax.annotation.Resource;
 import java.io.IOException;
-import java.io.InputStream;

 public class PkgScreenshotControllerIT extends AbstractIntegrationTest {

=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/repository/RepositoryImportServiceIT.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/repository/RepositoryImportServiceIT.java Mon Apr 7 11:46:03 2014 UTC
@@ -14,7 +14,7 @@
 import org.haikuos.haikudepotserver.dataobjects.Repository;
 import org.haikuos.haikudepotserver.pkg.model.PkgRepositoryImportJob;
 import org.haikuos.haikudepotserver.repository.RepositoryImportService;
-import org.haikuos.haikudepotsever.api1.support.AbstractIntegrationTest;
+import org.haikuos.haikudepotsever.AbstractIntegrationTest;
 import org.junit.Test;

 import javax.annotation.Resource;
=======================================
--- /haikudepotserver-webapp/src/test/resources/spring/test.xml Sat Jan 18 09:59:17 2014 UTC +++ /haikudepotserver-webapp/src/test/resources/spring/test.xml Mon Apr 7 11:46:03 2014 UTC
@@ -4,7 +4,7 @@
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd";>

- <bean class="org.haikuos.haikudepotsever.api1.support.IntegrationTestSupportService"> + <bean class="org.haikuos.haikudepotsever.IntegrationTestSupportService">
     </bean>

 </beans>

==============================================================================
Revision: 71a22530b2ca
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Tue Apr  8 10:24:23 2014 UTC
Log:      + initial work on package version localization

http://code.google.com/p/haiku-depot-web-app/source/detail?r=71a22530b2ca

Added:
 /haikudepotserver-webapp/src/main/webapp/css/editpkgversionlocalization.css
/haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalization.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalizationcontroller.js
Modified:
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java
 /haikudepotserver-webapp/src/main/resources/messages.properties
 /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml
 /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css
 /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html
/haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js
 /haikudepotserver-webapp/src/main/webapp/js/app/routes.js

=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/css/editpkgversionlocalization.css Tue Apr 8 10:24:23 2014 UTC
@@ -0,0 +1,67 @@
+#edit-pkg-version-localization-container {
+    position: relative;
+    border : 1px solid black;
+    border-collapse: collapse;
+    border: 1px black solid;
+    width: 100%
+}
+
+#edit-pkg-version-localization-container td {
+    padding-left: 20px;
+    padding-right: 20px;
+    padding-bottom: 20px;
+    padding-top: 20px;
+    vertical-align: top;
+}
+
+#edit-pkg-version-localization-container > tr > td {
+    border-top: 1px solid black;
+    border-bottom: 1px solid black;
+}
+
+#natural-translations-cell {
+    width: 16%;
+    border-right : 1px dotted lightgray;
+    border-left: 1px solid black;
+}
+
+#natural-translations-cell div {
+    padding: 4px;
+}
+
+#english-original-cell {
+    width: 30%;
+    border-left : 1px solid black;
+    border-right: 1px solid black;
+    background-color: #EEE;
+}
+
+#translation-cell {
+}
+
+#english-original-cell h1 {
+    text-align: right;
+    margin-top: 2px;
+    margin-bottom: 2px;
+    font-size: 10px;
+    color: grey;
+}
+
+#english-original-cell h2 {
+    margin-top: 4px;
+    margin-bottom:2px;
+}
+
+#english-original-cell p {
+    margin-top: 2px;
+    margin-bottom:8px;
+}
+
+#edit-pkg-version-localization-actions-container {
+    margin-top: 16px;
+    margin-left: 16%
+}
+
+.selected-translation {
+    font-weight: bold;
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalization.html Tue Apr 8 10:24:23 2014 UTC
@@ -0,0 +1,52 @@
+<breadcrumbs items="breadcrumbItems"></breadcrumbs>
+
+<div class="content-container">
+
+    <form name="editPkgVersionLocalizationForm" novalidate="novalidate">
+
+        <table id="edit-pkg-version-localization-container">
+            <tr>
+                <td id="natural-translations-cell">
+                    <div ng-repeat="translation in translations">
+                        <span ng-show="isTranslationSelected(translation)">
+                            &#8594;
+                        </span>
+                        <a
+ ng-class="classesForTranslation(translation)" + ng-click="goChooseTranslation(translation)"> + <message key="{{'naturalLanguage.' + translation.naturalLanguage.code}}"></message>
+                        </a>
+                        ({{translation.naturalLanguage.code}})
+                    </div>
+                </td>
+                <td id="translation-cell">
+                    <H1>WORK IN PROGRESS</H1>
+ <p>Lorem ipsum dolor sit amet, no dolore persecuti necessitatibus sed, eruditi mnesarchum eloquentiam pri in. Te sit lobortis eleifend, aperiam nominavi maluisset vel ut. Ex vim docendi expetendis voluptatum. Partem aliquam pro te, no doctus prompta repudiandae eum. No vix veniam aperiri, detraxit referrentur reprehendunt in duo.</p>
+                </td>
+                <td id="english-original-cell">
+                    <h1>English Original</h1>
+                    <h2>Summary</h2>
+                    <p>{{pkg.versions[0].summary}}</p>
+                    <h2>Description</h2>
+                    <p>{{pkg.versions[0].description}}</p>
+                </td>
+            </tr>
+        </table>
+
+        <div id="edit-pkg-version-localization-actions-container">
+            <button
+                    ng-disabled="editPkgVersionLocalizationForm.$invalid"
+                    ng-click="goStorePkgVersionLocalizations()"
+                    type="submit"
+                    class="main-action">
+ <message key="editPkgVersionLocalization.action.title"></message>
+            </button>
+        </div>
+
+    </form>
+
+</div>
+
+<div class="footer"></div>
+<spinner spin="shouldSpin()"></spinner>
+
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalizationcontroller.js Tue Apr 8 10:24:23 2014 UTC
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+angular.module('haikudepotserver').controller(
+    'EditPkgVersionLocalizationController',
+    [
+        '$scope','$log','$location','$routeParams',
+        'jsonRpc','constants','pkgIcon','errorHandling',
+        'breadcrumbs','userState','referenceData',
+        function(
+            $scope,$log,$location,$routeParams,
+            jsonRpc,constants,pkgIcon,errorHandling,
+            breadcrumbs,userState,referenceData) {
+
+            $scope.pkg = undefined;
+            $scope.amSaving = false;
+            $scope.translations = undefined;
+            $scope.selectedTranslation = undefined;
+            $scope.editPkgVersionLocalizations = {
+            };
+
+            $scope.shouldSpin = function() {
+ return undefined == $scope.pkg || $scope.amSaving || undefined == $scope.translations;
+            };
+
+            $scope.isTranslationSelected = function(translation) {
+ return $scope.selectedTranslation.naturalLanguage.code == translation.naturalLanguage.code;
+            }
+
+            /**
+ * <p>This colours the text of the language to indicate if the translation is missing, invalid or
+             * has been supplied successfully.</p>
+             */
+
+            $scope.classesForTranslation = function(translation) {
+                var classes = [];
+ var hasSummary = translation.summary && translation.summary.length; + var hasDescription = translation.description && translation.description.length;
+
+                if(hasDescription && hasSummary) {
+                    classes.push('text-success');
+                }
+                else {
+                    if(hasDescription != hasSummary) {
+                        classes.push('text-error');
+                    }
+                    else {
+                        classes.push('text-warning');
+                    }
+                }
+
+                if($scope.isTranslationSelected(translation)) {
+                    classes.push('selected-translation');
+                }
+
+                return classes;
+            }
+
+            $scope.goChooseTranslation = function(translation) {
+                if(!translation) {
+                    throw 'the translation must be provided to select';
+                }
+
+                $scope.selectedTranslation = translation;
+            }
+
+            $scope.deriveFormControlsContainerClasses = function(name) {
+ return $scope.editPkgIconForm[name].$invalid ? ['form-control-group-error'] : [];
+            };
+
+            function refreshBreadcrumbItems() {
+                breadcrumbs.mergeCompleteStack([
+                    breadcrumbs.createHome(),
+                    breadcrumbs.createViewPkg(
+                        $scope.pkg,
+                        $routeParams.version,
+                        $routeParams.architectureCode),
+                    {
+ titleKey : 'breadcrumb.editPkgVersionLocalizations.title',
+                        path : $location.path()
+                    }
+                ]);
+            }
+
+            function setupTranslations() {
+                referenceData.naturalLanguages().then(
+                    function(data) {
+                        $scope.translations = _.map(
+                            _.reject(
+                                data,
+                                function(d) {
+ return d.code == constants.NATURALLANGUAGECODE_ENGLISH;
+                                }
+                            ),
+                            function(d) {
+                                return {
+                                    naturalLanguage:d,
+                                    summary:'',
+                                    description:'',
+                                    wasEdited:false
+                                };
+                            }
+                        );
+
+ $scope.selectedTranslation = $scope.translations[0];
+
+                        $log.info('did setup translations');
+                    },
+                    function() {
+                        errorHandling.navigateToError();
+                    }
+                );
+            }
+
+ // pulls the pkg data back from the server so that it can be used to
+            // display the form.
+
+            function refetchPkg() {
+                jsonRpc.call(
+                    constants.ENDPOINT_API_V1_PKG,
+                    'getPkg',
+                    [{
+                        name: $routeParams.name,
+                        versionType : 'LATEST',
+                        incrementViewCounter : false,
+                        architectureCode : $routeParams.architectureCode,
+ naturalLanguageCode: constants.NATURALLANGUAGECODE_ENGLISH
+                    }]
+                ).then(
+                    function(result) {
+                        $scope.pkg = result;
+                        $log.info('found '+result.name+' pkg');
+                        refreshBreadcrumbItems();
+                    },
+                    function(err) {
+                        errorHandling.handleJsonRpcError(err);
+                    }
+                );
+            }
+
+            refetchPkg();
+            setupTranslations();
+
+        }
+    ]
+);
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Tue Apr 8 10:24:23 2014 UTC
@@ -730,7 +730,7 @@

         User authUser = obtainAuthenticatedUser(context);

- if(!authorizationService.check(context, authUser, pkg, Permission.PKG_EDITLOCALIZATION)) { + if(!authorizationService.check(context, authUser, pkg, Permission.PKG_EDITVERSIONLOCALIZATION)) {
             throw new AuthorizationFailureException();
         }

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Sat Apr 5 10:49:12 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Tue Apr 8 10:24:23 2014 UTC
@@ -151,7 +151,7 @@
             case PKG_EDITICON:
             case PKG_EDITSCREENSHOT:
             case PKG_EDITCATEGORIES:
-            case PKG_EDITLOCALIZATION:
+            case PKG_EDITVERSIONLOCALIZATION:
return null!=authenticatedUser && authenticatedUser.getIsRoot();

             default:
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Sat Apr 5 10:49:12 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java Tue Apr 8 10:24:23 2014 UTC
@@ -22,7 +22,7 @@
     PKG_EDITICON(TargetType.PKG),
     PKG_EDITSCREENSHOT(TargetType.PKG),
     PKG_EDITCATEGORIES(TargetType.PKG),
-    PKG_EDITLOCALIZATION(TargetType.PKG);
+    PKG_EDITVERSIONLOCALIZATION(TargetType.PKG);

     private TargetType requiredTargetType;

=======================================
--- /haikudepotserver-webapp/src/main/resources/messages.properties Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages.properties Tue Apr 8 10:24:23 2014 UTC
@@ -28,6 +28,7 @@
 breadcrumb.editPkgCategories.title=Categories
 breadcrumb.editPkgIcon.title=Icon
 breadcrumb.editPkgScreenshots.title=Screenshots
+breadcrumb.editPkgVersionLocalizations.title=Localization
 breadcrumb.editUser.title=Edit

 about.title=About
@@ -89,6 +90,8 @@
 editPkgIcon.iconHvifFile.clearAction.title=Clear
 editPkgIcon.action.title=Store Icons

+editPkgVersionLocalization.action.title=Store Edited Localizations
+
 home.viewCriteriaType.all=All
 home.viewCriteriaType.search=Search
 home.viewCriteriaType.categories=Categories
@@ -146,6 +149,7 @@
 viewPkg.downloadIconHvifAction.title=Download icon in 'hvif' format
 viewPkg.editScreenshotsAction.title=Edit screenshots
 viewPkg.editPkgCategoriesAction.title=Edit categories
+viewPkg.editVersionLocalizationAction.title=Edit localizations

 viewRepository.importTriggered.title=Import triggered
viewRepository.importTriggered.description=the application will import the repository's data soon.
=======================================
--- /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Tue Apr 8 10:24:23 2014 UTC
@@ -80,6 +80,7 @@
<value>/js/app/controller/editpkgscreenshotscontroller.js</value> <value>/js/app/controller/editpkgcategoriescontroller.js</value> <value>/js/app/controller/editusercontroller.js</value> + <value>/js/app/controller/editpkgversionlocalizationcontroller.js</value>

<value>/js/app/service/jsonrpcservice.js</value> <value>/js/app/service/messagesourceservice.js</value>
@@ -116,6 +117,7 @@
                             <value>/css/banner.css</value>
                             <value>/css/breadcrumbs.css</value>
                             <value>/css/editpkgscreenshots.css</value>
+ <value>/css/editpkgversionlocalization.css</value>
                         </list>
                     </property>
                 </bean>
=======================================
--- /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css Tue Apr 8 10:24:23 2014 UTC
@@ -55,6 +55,23 @@
     background-color: rgba(0, 0, 0, 0.6);
     z-index: 1050;
 }
+
+/*
+==================================
+TEXT STYLING
+*/
+
+.text-error {
+    color: red;
+}
+
+.text-warning {
+    color: darkorange;
+}
+
+.text-success {
+    color: darkgreen;
+}

 /*
 ==================================
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html Tue Apr 8 10:24:23 2014 UTC
@@ -114,6 +114,11 @@
                     <a href="" ng-click="goEditScreenshots()">
<message key="viewPkg.editScreenshotsAction.title"></message>
                     </a>
+                </li>
+ <li pkg="pkg" show-if-pkg-permission="'PKG_EDITVERSIONLOCALIZATION'">
+                    <a href="" ng-click="goEditVersionLocalization()">
+ <message key="viewPkg.editVersionLocalizationAction.title"></message>
+                    </a>
                 </li>
<li pkg="pkg" show-if-pkg-permission="'PKG_EDITCATEGORIES'">
                     <a href="" ng-click="goEditPkgCategories()">
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Tue Apr 8 10:24:23 2014 UTC
@@ -206,6 +206,10 @@
$location.path($location.path() + '/editscreenshots').search({});
             };

+            $scope.goEditVersionLocalization = function() {
+ $location.path($location.path() + '/editversionlocalizations').search({});
+            }
+
             $scope.goEditPkgCategories = function() {
$location.path($location.path() + '/editcategories').search({});
             };
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Tue Apr 8 10:24:23 2014 UTC
@@ -23,6 +23,7 @@
.when('/pkg/:name/:version/:architectureCode/editicon',{controller:'EditPkgIconController', templateUrl:'/js/app/controller/editpkgicon.html'}) .when('/pkg/:name/:version/:architectureCode/editscreenshots',{controller:'EditPkgScreenshotsController', templateUrl:'/js/app/controller/editpkgscreenshots.html'}) .when('/pkg/:name/:version/:architectureCode/editcategories',{controller:'EditPkgCategoriesController', templateUrl:'/js/app/controller/editpkgcategories.html'}) + .when('/pkg/:name/:version/:architectureCode/editversionlocalizations',{controller:'EditPkgVersionLocalizationController', templateUrl:'/js/app/controller/editpkgversionlocalization.html'}) .when('/error',{controller:'ErrorController', templateUrl:'/js/app/controller/error.html'})
                 
.when('/',{controller:'HomeController',templateUrl:'/js/app/controller/home.html'})
.otherwise({controller:'OtherwiseController', template: '<div></div>'});

==============================================================================
Revision: 5dd8c8e0ff1d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Wed Apr  9 11:01:12 2014 UTC
Log: + work on a bulk retreival system for the haiku depot server desktop client

http://code.google.com/p/haiku-depot-web-app/source/detail?r=5dd8c8e0ff1d

Added:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetBulkPkgRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetBulkPkgResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgIcon.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgScreenshot.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgVersionType.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgVersionUrl.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/LimitExceededException.java
Modified:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetRuntimeInformationResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgIconsResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/SearchPkgsResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/PkgSearchSpecification.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java

=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetBulkPkgRequest.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,29 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+import java.util.List;
+
+public class GetBulkPkgRequest {
+
+    public enum Filter {
+        PKGSCREENSHOTS,
+        PKGCATEGORIES,
+        PKGICONS,
+        USERRATINGAVERAGES,
+    };
+
+    public List<String> pkgNames;
+
+    public String architectureCode;
+
+    public PkgVersionType versionType;
+
+    public String naturalLanguageCode;
+
+    public List<Filter> filter;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetBulkPkgResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,60 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+import java.util.List;
+
+public class GetBulkPkgResult {
+
+    public List<Pkg> pkgs;
+
+    public static class Pkg {
+
+        public String name;
+
+        /**
+ * <p>This is the timestamp (millis since epoc) at which the package was last edited. This is helpful for + * situations where it is necessary to create a url that will cause the browser to refresh the data.</p>
+         */
+
+        public Long modifyTimestamp;
+
+        public List<GetBulkPkgResult.PkgVersion> versions;
+
+        public List<String> pkgCategoryCodes;
+
+        public List<PkgScreenshot> pkgScreenshots;
+
+        public List<PkgIcon> pkgIcons;
+
+        public Float userRatingAverage;
+
+    }
+
+    public static class PkgVersion {
+
+        public String major;
+        public String minor;
+        public String micro;
+        public String preRelease;
+        public Integer revision;
+
+        /**
+ * <p>In the request the client may have requested a specific natural language, but that may not have been + * available. This code indicates the natural language code that was <em>actually</em> used to obtain
+         * material such as the summary and the description.</p>
+         */
+
+        public String naturalLanguageCode;
+
+        public String summary;
+        public String description;
+
+        public Float userRatingAverage;
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgIcon.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,21 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+public class PkgIcon {
+
+    public String mediaTypeCode;
+    public Integer size;
+
+    public PkgIcon() {
+    }
+
+    public PkgIcon(String mediaTypeCode, Integer size) {
+        this.mediaTypeCode = mediaTypeCode;
+        this.size = size;
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgScreenshot.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,13 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+public class PkgScreenshot {
+    public String code;
+    public Integer length;
+    public Integer height;
+    public Integer width;
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgVersionType.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,16 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+/**
+ * <p>This type defines the versions that should be sent back in the result. If the client were + * only interested in the latest version for example, then it should use the "LATEST" value.</p>
+ */
+
+public enum PkgVersionType {
+    LATEST,
+    NONE
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/PkgVersionUrl.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,20 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.model.pkg;
+
+public class PkgVersionUrl {
+
+    public String url;
+    public String urlTypeCode;
+
+    public PkgVersionUrl() {
+    }
+
+    public PkgVersionUrl(String urlTypeCode, String url) {
+        this.url = url;
+        this.urlTypeCode = urlTypeCode;
+    }
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/LimitExceededException.java Wed Apr 9 11:01:12 2014 UTC
@@ -0,0 +1,17 @@
+/*
+* Copyright 2014, Andrew Lindesay
+* Distributed under the terms of the MIT License.
+*/
+
+package org.haikuos.haikudepotserver.api1.support;
+
+/**
+ * <p>This is thrown in the API when the user has requested some data and has exceeded a pre-agreed limit.</p>
+ */
+
+public class LimitExceededException extends Exception {
+
+    public LimitExceededException() {
+    }
+
+}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Sat Apr 5 10:49:12 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Wed Apr 9 11:01:12 2014 UTC
@@ -8,6 +8,7 @@
 import com.googlecode.jsonrpc4j.JsonRpcService;
 import org.haikuos.haikudepotserver.api1.model.pkg.*;
 import org.haikuos.haikudepotserver.api1.support.BadPkgIconException;
+import org.haikuos.haikudepotserver.api1.support.LimitExceededException;
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;

 /**
@@ -17,6 +18,8 @@
 @JsonRpcService("/api/v1/pkg")
 public interface PkgApi {

+    public final static Integer GETBULKPKG_LIMIT = 50;
+
     /**
* <p>This method will ensure that the categories configured on the nominated package are as per the list of
      * packages.</p>
@@ -106,4 +109,27 @@

GetPkgVersionLocalizationsResult getPkgVersionLocalizations(GetPkgVersionLocalizationsRequest getPkgVersionLocalizationsRequest) throws ObjectNotFoundException;

+    /**
+ * <p>This method will obtain data about some named packages. Note that the quantity of packages requested should + * not exceed {@link #GETBULKPKG_LIMIT}; if it does exceed this limit then an instance of + * {@link org.haikuos.haikudepotserver.api1.support.LimitExceededException} will be thrown.</p>
+     *
+     * <p>This limit can be obtained from the
+ * {@link org.haikuos.haikudepotserver.api1.MiscellaneousApi#getRuntimeInformation(org.haikuos.haikudepotserver.api1.model.miscellaneous.GetRuntimeInformationRequest)}
+     * method.
+     * </p>
+     *
+ * <p>If a package was not able to be found then it will simply not appear in the results. If reference data + * objects such as the architecture was unable to be found then this method will throw an instance of + * {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException}.</p>
+     *
+ * <p>The definition of architecture on this method is strict; will only return data for which there is
+     * a version on that architecture.</p>
+     *
+ * <p>Various elements of the response can be filtered in or out by using the filter attribute on the request
+     * object.</p>
+     */
+
+ GetBulkPkgResult getBulkPkg(GetBulkPkgRequest getBulkPkgRequest) throws LimitExceededException, ObjectNotFoundException;
+
 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetRuntimeInformationResult.java Sun Feb 2 09:15:35 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetRuntimeInformationResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -13,4 +13,6 @@

     public Long startTimestamp;

+    public Integer getBulkPkgRequestLimit;
+
 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgIconsResult.java Mon Feb 24 10:10:13 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgIconsResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -9,13 +9,6 @@

 public class GetPkgIconsResult {

-    public List<GetPkgIconsResult.PkgIcon> pkgIcons;
-
-    public static class PkgIcon {
-
-        public String mediaTypeCode;
-        public Integer size;
-
-    }
+    public List<PkgIcon> pkgIcons;

 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgRequest.java Wed Apr 9 11:01:12 2014 UTC
@@ -6,16 +6,6 @@
 package org.haikuos.haikudepotserver.api1.model.pkg;

 public class GetPkgRequest {
-
-    /**
- * <p>This type defines the versions that should be sent back in the result. If the client were - * only interested in the latest version for example, then it should use the "LATEST" value.</p>
-     */
-
-    public enum VersionType {
-        LATEST,
-        NONE
-    }

     /**
      * <p>This is the name of the package that you wish to obtain.</p>
@@ -27,7 +17,7 @@
* <p>If this is true then the counter on the version is incremented; indicating that the package has been * seen. Do not use this unless the user is being displayed a user-interface of the package so that they * have <em>really</em> seen it. This value may be supplied as null. This only applies when the - * {@link org.haikuos.haikudepotserver.api1.model.pkg.GetPkgRequest.VersionType#LATEST} version type is
+     * {@link PkgVersionType#LATEST} version type is
* being requested. Also note that the system has a feature to avoid double counting from the same address in
      * quick succession.</p>
      */
@@ -41,7 +31,7 @@

     public String architectureCode;

-    public VersionType versionType;
+    public PkgVersionType versionType;

     public String naturalLanguageCode;

=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java Fri Apr 4 10:13:57 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -22,11 +22,11 @@

     public Long modifyTimestamp;

-    public List<Version> versions;
+    public List<GetPkgResult.PkgVersion> versions;

     public List<String> pkgCategoryCodes;

-    public static class Version {
+    public static class PkgVersion {

         public String major;
         public String minor;
@@ -50,16 +50,11 @@

         public List<String> licenses;
         public List<String> copyrights;
-        public List<Url> urls;
+        public List<PkgVersionUrl> urls;

         public Long viewCounter;

-    }
-
-    public static class Url {
-
-        public String url;
-        public String urlTypeCode;
+        public Float userRatingAverage;

     }

=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java Tue Feb 18 09:54:04 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -11,11 +11,4 @@

     public List<PkgScreenshot> items;

-    public static class PkgScreenshot {
-        public String code;
-        public Integer length;
-        public Integer height;
-        public Integer width;
-    }
-
 }
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/SearchPkgsResult.java Wed Mar 12 11:16:46 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/SearchPkgsResult.java Wed Apr 9 11:01:12 2014 UTC
@@ -12,10 +12,10 @@
     public static class Pkg {
         public String name;
         public Long modifyTimestamp;
-        public Version version;
+        public SearchPkgsResult.PkgVersion version;
     }

-    public static class Version {
+    public static class PkgVersion {
         public String major;
         public String minor;
         public String micro;
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java Mon Feb 24 08:20:27 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java Wed Apr 9 11:01:12 2014 UTC
@@ -12,5 +12,6 @@
     public final static int ERROR_CODE_CAPTCHABADRESPONSE = -32802;
     public final static int ERROR_CODE_AUTHORIZATIONFAILURE = -32803;
     public final static int ERROR_CODE_BADPKGICON = -32804;
+    public final static int ERROR_CODE_LIMITEXCEEDED = -32805;

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Sun Mar 23 10:09:17 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Wed Apr 9 11:01:12 2014 UTC
@@ -144,6 +144,7 @@

GetRuntimeInformationResult result = new GetRuntimeInformationResult(); result.projectVersion = runtimeInformationService.getProjectVersion();
+        result.getBulkPkgRequestLimit = PkgApi.GETBULKPKG_LIMIT;

if(authUserOptional.isPresent() && authUserOptional.get().getIsRoot()) { result.javaVersion = runtimeInformationService.getJavaVersion();
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Tue Apr 8 10:24:23 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Wed Apr 9 11:01:12 2014 UTC
@@ -13,13 +13,19 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.googlecode.jsonrpc4j.Base64;
+import org.apache.cayenne.CayenneDataObject;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.query.PrefetchTreeNode;
 import org.haikuos.haikudepotserver.api1.model.pkg.*;
import org.haikuos.haikudepotserver.api1.support.AuthorizationFailureException;
 import org.haikuos.haikudepotserver.api1.support.BadPkgIconException;
+import org.haikuos.haikudepotserver.api1.support.LimitExceededException;
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
 import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
+import org.haikuos.haikudepotserver.dataobjects.PkgVersionUrl;
 import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
 import org.haikuos.haikudepotserver.pkg.model.PkgSearchSpecification;
 import org.haikuos.haikudepotserver.security.AuthorizationService;
@@ -34,6 +40,7 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;

@@ -226,7 +233,8 @@
         specification.setOffset(request.offset);

         SearchPkgsResult result = new SearchPkgsResult();
- List<PkgVersion> searchedPkgVersions = pkgService.search(context,specification);
+
+ List<PkgVersion> searchedPkgVersions = pkgService.search(context,specification,null);

// if there are more than we asked for then there must be more available.

@@ -246,7 +254,7 @@
                         resultPkg.name = input.getPkg().getName();
resultPkg.modifyTimestamp = input.getPkg().getModifyTimestamp().getTime();

- SearchPkgsResult.Version resultVersion = new SearchPkgsResult.Version(); + SearchPkgsResult.PkgVersion resultVersion = new SearchPkgsResult.PkgVersion();
                         resultVersion.major = input.getMajor();
                         resultVersion.minor = input.getMinor();
                         resultVersion.micro = input.getMicro();
@@ -271,8 +279,11 @@
* <p>Given the persistence model object, this method will construct the DTO to be sent back over the wire.</p>
      */

- private GetPkgResult.Version createVersion(PkgVersion pkgVersion, NaturalLanguage naturalLanguage) {
-        GetPkgResult.Version version = new GetPkgResult.Version();
+ private GetPkgResult.PkgVersion createGetPkgResultPkgVersion(PkgVersion pkgVersion, NaturalLanguage naturalLanguage) {
+        Preconditions.checkNotNull(pkgVersion);
+        Preconditions.checkNotNull(naturalLanguage);
+
+        GetPkgResult.PkgVersion version = new GetPkgResult.PkgVersion();

         version.major = pkgVersion.getMajor();
         version.minor = pkgVersion.getMinor();
@@ -318,10 +329,10 @@

         version.urls = Lists.transform(
                 pkgVersion.getPkgVersionUrls(),
-                new Function<PkgVersionUrl, GetPkgResult.Url>() {
+ new Function<PkgVersionUrl, org.haikuos.haikudepotserver.api1.model.pkg.PkgVersionUrl>() {
                     @Override
- public GetPkgResult.Url apply(org.haikuos.haikudepotserver.dataobjects.PkgVersionUrl input) {
-                        GetPkgResult.Url url = new GetPkgResult.Url();
+ public org.haikuos.haikudepotserver.api1.model.pkg.PkgVersionUrl apply(PkgVersionUrl input) { + org.haikuos.haikudepotserver.api1.model.pkg.PkgVersionUrl url = new org.haikuos.haikudepotserver.api1.model.pkg.PkgVersionUrl();
                         url.url = input.getUrl();
                         url.urlTypeCode = input.getPkgUrlType().getCode();
                         return url;
@@ -417,7 +428,7 @@

                 }

-                result.versions = Collections.singletonList(createVersion(
+ result.versions = Collections.singletonList(createGetPkgResultPkgVersion(
                         pkgVersionOptional.get(),
                         naturalLanguage));

@@ -445,7 +456,7 @@
return Iterables.tryFind(pkgIconApis, new Predicate<ConfigurePkgIconRequest.PkgIcon>() {
             @Override
             public boolean apply(ConfigurePkgIconRequest.PkgIcon input) {
- return input.mediaTypeCode.equals(mediaTypeCode) && (null!=input.size) && (input.size == size); + return input.mediaTypeCode.equals(mediaTypeCode) && (null!=input.size) && (input.size.equals(size));
             }
         }).isPresent();

@@ -462,13 +473,12 @@
         GetPkgIconsResult result = new GetPkgIconsResult();
         result.pkgIcons = Lists.transform(
                 pkg.getPkgIcons(),
-                new Function<PkgIcon, GetPkgIconsResult.PkgIcon>() {
+ new Function<org.haikuos.haikudepotserver.dataobjects.PkgIcon, org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
                     @Override
-                    public GetPkgIconsResult.PkgIcon apply(PkgIcon input) {
- GetPkgIconsResult.PkgIcon apiPkgIcon = new GetPkgIconsResult.PkgIcon();
-                        apiPkgIcon.size = input.getSize();
- apiPkgIcon.mediaTypeCode = input.getMediaType().getCode();
-                        return apiPkgIcon;
+ public org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon apply(org.haikuos.haikudepotserver.dataobjects.PkgIcon input) { + return new org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon(
+                                input.getMediaType().getCode(),
+                                input.getSize());
                     }
                 }
         );
@@ -496,7 +506,7 @@
         int updated = 0;
         int removed = 0;

-        Set<PkgIcon> createdOrUpdatedPkgIcons = Sets.newHashSet();
+ Set<org.haikuos.haikudepotserver.dataobjects.PkgIcon> createdOrUpdatedPkgIcons = Sets.newHashSet();

         if(null!=request.pkgIcons && !request.pkgIcons.isEmpty()) {

@@ -551,7 +561,7 @@

// now we have some icons stored which may not be in the replacement data; we should remove those ones.

-        for(PkgIcon pkgIcon : ImmutableList.copyOf(pkg.getPkgIcons())) {
+ for(org.haikuos.haikudepotserver.dataobjects.PkgIcon pkgIcon : ImmutableList.copyOf(pkg.getPkgIcons())) {
             if(!createdOrUpdatedPkgIcons.contains(pkgIcon)) {
                 context.deleteObjects(
                         pkgIcon.getPkgIconImage().get(),
@@ -567,11 +577,9 @@

         logger.info(
                 "did configure icons for pkg {} (updated {}, removed {})",
-                new Object[] {
-                        pkg.getName(),
-                        updated,
-                        removed
-                }
+                pkg.getName(),
+                updated,
+                removed
         );

         return new ConfigurePkgIconResult();
@@ -593,7 +601,7 @@
             throw new AuthorizationFailureException();
         }

-        for(PkgIcon pkgIcon : ImmutableList.copyOf(pkg.getPkgIcons())) {
+ for(org.haikuos.haikudepotserver.dataobjects.PkgIcon pkgIcon : ImmutableList.copyOf(pkg.getPkgIcons())) {
             context.deleteObjects(
                     pkgIcon.getPkgIconImage().get(),
                     pkgIcon);
@@ -627,6 +635,16 @@
         result.length = pkgScreenshotOptional.get().getLength();
         return result;
     }
+
+ private org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot createPkgScreenshot(PkgScreenshot pkgScreenshot) {
+        Preconditions.checkNotNull(pkgScreenshot);
+ org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot rs = new org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot();
+        rs.code = pkgScreenshot.getCode();
+        rs.height = pkgScreenshot.getHeight();
+        rs.width = pkgScreenshot.getWidth();
+        rs.length = pkgScreenshot.getLength();
+        return rs;
+    }

     @Override
public GetPkgScreenshotsResult getPkgScreenshots(GetPkgScreenshotsRequest getPkgScreenshotsRequest) throws ObjectNotFoundException {
@@ -634,20 +652,15 @@
Preconditions.checkState(!Strings.isNullOrEmpty(getPkgScreenshotsRequest.pkgName));

         final ObjectContext context = serverRuntime.getContext();
-        Pkg pkg = getPkg(context, getPkgScreenshotsRequest.pkgName);
+        final Pkg pkg = getPkg(context, getPkgScreenshotsRequest.pkgName);

         GetPkgScreenshotsResult result = new GetPkgScreenshotsResult();
         result.items = Lists.transform(
                 pkg.getSortedPkgScreenshots(),
- new Function<PkgScreenshot, GetPkgScreenshotsResult.PkgScreenshot>() { + new Function<PkgScreenshot, org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot>() {
                     @Override
- public GetPkgScreenshotsResult.PkgScreenshot apply(PkgScreenshot pkgScreenshot) { - GetPkgScreenshotsResult.PkgScreenshot rs = new GetPkgScreenshotsResult.PkgScreenshot();
-                        rs.code = pkgScreenshot.getCode();
-                        rs.height = pkgScreenshot.getHeight();
-                        rs.width = pkgScreenshot.getWidth();
-                        rs.length = pkgScreenshot.getLength();
-                        return rs;
+ public org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot apply(PkgScreenshot pkgScreenshot) {
+                        return createPkgScreenshot(pkgScreenshot);
                     }
                 }
         );
@@ -778,11 +791,12 @@

         context.commitChanges();

- logger.info("did update the localization for pkg {} in architecture {} for natural language {}",new Object[] {
+        logger.info(
+ "did update the localization for pkg {} in architecture {} for natural language {}",
                 pkg.getName(),
                 architecture.getCode(),
                 naturalLanguage.getCode()
-        });
+        );

         return new UpdatePkgVersionLocalizationResult();
     }
@@ -818,6 +832,200 @@
result.pkgVersionLocalizations.add(resultPkgVersionLocalization);
             }
         }
+
+        return result;
+    }
+
+ // this is here temporarily until user ratings are actually implemented.
+    // TODO; REMOVE
+ private Float randomizedRatingAverage__temporary(CayenneDataObject dataObject) { + long pk = ((Number) dataObject.getObjectId().getIdSnapshot().get("id")).longValue();
+        Random random = new Random(pk);
+        return Math.abs(random.nextFloat() % 5);
+    }
+
+    private GetBulkPkgResult.PkgVersion createGetBulkPkgResultPkgVersion(
+            PkgVersion pkgVersion,
+            NaturalLanguage naturalLanguage) {
+
+        Preconditions.checkNotNull(pkgVersion);
+        Preconditions.checkNotNull(naturalLanguage);
+
+ GetBulkPkgResult.PkgVersion version = new GetBulkPkgResult.PkgVersion();
+
+        version.major = pkgVersion.getMajor();
+        version.minor = pkgVersion.getMinor();
+        version.micro = pkgVersion.getMicro();
+        version.revision = pkgVersion.getRevision();
+        version.preRelease = pkgVersion.getPreRelease();
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersion.getPkgVersionLocalization(naturalLanguage);
+
+        if(!pkgVersionLocalizationOptional.isPresent()) {
+ if(!naturalLanguage.getCode().equals(NaturalLanguage.CODE_ENGLISH)) { + pkgVersionLocalizationOptional = pkgVersion.getPkgVersionLocalization(NaturalLanguage.CODE_ENGLISH);
+            }
+        }
+
+        if(pkgVersionLocalizationOptional.isPresent()) {
+ version.description = pkgVersionLocalizationOptional.get().getDescription(); + version.summary = pkgVersionLocalizationOptional.get().getSummary(); + version.naturalLanguageCode = pkgVersionLocalizationOptional.get().getNaturalLanguage().getCode();
+        }
+
+        return version;
+
+    }
+
+    @Override
+ public GetBulkPkgResult getBulkPkg(final GetBulkPkgRequest getBulkPkgRequest) throws LimitExceededException, ObjectNotFoundException {
+        Preconditions.checkNotNull(getBulkPkgRequest);
+        Preconditions.checkNotNull(getBulkPkgRequest.architectureCode);
+        Preconditions.checkNotNull(getBulkPkgRequest.pkgNames);
+
+        if(getBulkPkgRequest.pkgNames.size() > GETBULKPKG_LIMIT) {
+            throw new LimitExceededException();
+        }
+
+        final GetBulkPkgResult result = new GetBulkPkgResult();
+
+        if(getBulkPkgRequest.pkgNames.isEmpty()) {
+            result.pkgs = Collections.emptyList();
+        }
+        else {
+
+            final ObjectContext context = serverRuntime.getContext();
+ final Architecture architecture = getArchitecture(context, getBulkPkgRequest.architectureCode); + final NaturalLanguage naturalLanguage = getNaturalLanguage(context, getBulkPkgRequest.naturalLanguageCode);
+
+            if(null==getBulkPkgRequest.filter) {
+                getBulkPkgRequest.filter = Collections.emptyList();
+            }
+
+ // use a pre-fetch tree in order to optimize the haul of data back into the application server from
+            // the database depending on what is being asked for.
+
+            PrefetchTreeNode prefetchTreeNode = new PrefetchTreeNode();
+
+ prefetchTreeNode.addPath(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY);
+
+ for(GetBulkPkgRequest.Filter filter : getBulkPkgRequest.filter) {
+                switch(filter) {
+                    case PKGSCREENSHOTS:
+ prefetchTreeNode.addPath(Joiner.on(Entity.PATH_SEPARATOR).join( + ImmutableList.of(PkgVersion.PKG_PROPERTY, Pkg.PKG_SCREENSHOTS_PROPERTY)));
+                        break;
+
+                    case PKGCATEGORIES:
+ prefetchTreeNode.addPath(Joiner.on(Entity.PATH_SEPARATOR).join( + ImmutableList.of(PkgVersion.PKG_PROPERTY, Pkg.PKG_PKG_CATEGORIES_PROPERTY)));
+                        break;
+
+                    case PKGICONS:
+ prefetchTreeNode.addPath(Joiner.on(Entity.PATH_SEPARATOR).join( + ImmutableList.of(PkgVersion.PKG_PROPERTY, Pkg.PKG_ICONS_PROPERTY)));
+                        break;
+                }
+            }
+
+            // now search the data.
+ PkgSearchSpecification searchSpecification = new PkgSearchSpecification();
+            searchSpecification.setArchitecture(architecture);
+            searchSpecification.setPkgNames(getBulkPkgRequest.pkgNames);
+            searchSpecification.setLimit(0);
+            searchSpecification.setLimit(Integer.MAX_VALUE);
+
+            long preFetchMs = System.currentTimeMillis();
+ final List<PkgVersion> pkgVersions = pkgService.search(context, searchSpecification, prefetchTreeNode);
+            long postFetchMs = System.currentTimeMillis();
+
+            // now return the data as necessary.
+            result.pkgs = Lists.transform(
+                    pkgVersions,
+                    new Function<PkgVersion, GetBulkPkgResult.Pkg>() {
+                        @Override
+ public GetBulkPkgResult.Pkg apply(PkgVersion input) {
+
+ GetBulkPkgResult.Pkg resultPkg = new GetBulkPkgResult.Pkg(); + resultPkg.modifyTimestamp = input.getPkg().getModifyTimestamp().getTime();
+                            resultPkg.name = input.getPkg().getName();
+
+ if(getBulkPkgRequest.filter.contains(GetBulkPkgRequest.Filter.PKGICONS)) {
+                                resultPkg.pkgIcons = Lists.transform(
+                                        input.getPkg().getPkgIcons(),
+ new Function<org.haikuos.haikudepotserver.dataobjects.PkgIcon, org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
+                                            @Override
+ public org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon apply(org.haikuos.haikudepotserver.dataobjects.PkgIcon pkgIcon) { + return new org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon( + pkgIcon.getMediaType().getCode(),
+                                                        pkgIcon.getSize());
+                                            }
+                                        }
+                                );
+                            }
+
+ if(getBulkPkgRequest.filter.contains(GetBulkPkgRequest.Filter.PKGSCREENSHOTS)) {
+                                resultPkg.pkgScreenshots = Lists.transform(
+ input.getPkg().getSortedPkgScreenshots(), + new Function<PkgScreenshot, org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot>() {
+                                            @Override
+ public org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot apply(PkgScreenshot pkgScreenshot) { + return createPkgScreenshot(pkgScreenshot);
+                                            }
+                                        }
+                                );
+                            }
+
+ if(getBulkPkgRequest.filter.contains(GetBulkPkgRequest.Filter.PKGCATEGORIES)) { + resultPkg.pkgCategoryCodes = Lists.transform( + input.getPkg().getPkgPkgCategories(), + new Function<PkgPkgCategory, String>() {
+                                            @Override
+ public String apply(PkgPkgCategory input) { + return input.getPkgCategory().getCode();
+                                            }
+                                        }
+                                );
+                            }
+
+ // TODO; put real values in once they are available. + if(getBulkPkgRequest.filter.contains(GetBulkPkgRequest.Filter.USERRATINGAVERAGES)) { + resultPkg.userRatingAverage = randomizedRatingAverage__temporary(input.getPkg());
+                            }
+
+                            switch(getBulkPkgRequest.versionType) {
+                                case LATEST:
+                                {
+ GetBulkPkgResult.PkgVersion resultPkgVersion = createGetBulkPkgResultPkgVersion(input, naturalLanguage);
+
+ // TODO; put real values in once they are available. + if(getBulkPkgRequest.filter.contains(GetBulkPkgRequest.Filter.USERRATINGAVERAGES)) { + resultPkgVersion.userRatingAverage = randomizedRatingAverage__temporary(input);
+                                    }
+
+ resultPkg.versions = Collections.singletonList(resultPkgVersion);
+                                }
+                                break;
+
+ case NONE: // no package version data available.
+                                    break;
+
+                                default:
+ throw new IllegalStateException("unsupported version type; "+getBulkPkgRequest.versionType.name());
+                            }
+
+                            return resultPkg;
+                        }
+                    }
+            );
+
+            logger.info(
+ "did search and find {} pkg versions for get bulk pkg; fetch in {}ms, marshall in {}ms",
+                    pkgVersions.size(),
+                    postFetchMs - preFetchMs,
+                    System.currentTimeMillis()-postFetchMs);
+
+        }

         return result;
     }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java Mon Feb 24 08:20:27 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java Wed Apr 9 11:01:12 2014 UTC
@@ -29,6 +29,13 @@
     @Override
public JsonError resolveError(Throwable t, Method method, List<JsonNode> arguments) {

+        if(LimitExceededException.class.isAssignableFrom(t.getClass())) {
+            return new JsonError(
+                    Constants.ERROR_CODE_LIMITEXCEEDED,
+                    "limitexceeded",
+                    null);
+        }
+
         // output for authorization failure.

if(AuthorizationFailureException.class.isAssignableFrom(t.getClass())) {
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Wed Apr 9 11:01:12 2014 UTC
@@ -22,7 +22,6 @@
 import org.haikuos.haikudepotserver.dataobjects.auto._PkgVersion;
import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;
 import org.haikuos.haikudepotserver.support.VersionCoordinates;
-import sun.tools.tree.AndExpression;

 import java.util.List;
 import java.util.regex.Pattern;
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Wed Apr 9 11:01:12 2014 UTC
@@ -5,6 +5,7 @@

 package org.haikuos.haikudepotserver.pkg;

+import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -98,7 +99,12 @@
     // ------------------------------
     // SEARCH

- public List<PkgVersion> search(ObjectContext context, PkgSearchSpecification search) {
+    /**
+ * <p>This performs a search on the packages. Note that the prefetch tree node that is supplied is relative to
+     * the package version.</p>
+     */
+
+ public List<PkgVersion> search(ObjectContext context, PkgSearchSpecification search, PrefetchTreeNode prefetchTreeNode) {
         Preconditions.checkNotNull(search);
         Preconditions.checkNotNull(context);
         Preconditions.checkState(search.getOffset() >= 0);
@@ -106,6 +112,10 @@
         Preconditions.checkNotNull(search.getArchitecture());
Preconditions.checkState(null==search.getDaysSinceLatestVersion() | | search.getDaysSinceLatestVersion().intValue() > 0);

+        if(null!=search.getPkgNames() && search.getPkgNames().isEmpty()) {
+            return Collections.emptyList();
+        }
+
// unfortunately this one became too complex to get working properly in JPQL; had to resort
         // to using raw SQL.

@@ -151,6 +161,22 @@
                 queryBuilder.append(" AND pc.code = ?");
                 parameters.add(search.getPkgCategory().getCode());
             }
+
+            if(null!=search.getPkgNames()) {
+                List<String> pn = search.getPkgNames();
+
+                queryBuilder.append(" AND p.name IN (");
+
+                for(int i=0;i<pn.size();i++) {
+                    if(0!=i) {
+                        queryBuilder.append(',');
+                    }
+                    queryBuilder.append('?');
+                    parameters.add(pn.get(i));
+                }
+
+                queryBuilder.append(")");
+            }

// make sure that we are dealing with the latest version in the package.

@@ -171,25 +197,28 @@
             queryBuilder.append(" ,pv2.revision DESC NULLS LAST");
             queryBuilder.append(" LIMIT 1");
             queryBuilder.append(")");
-            queryBuilder.append(" ORDER BY");

-            switch(search.getSortOrdering()) {
+            if(null!=search.getSortOrdering()) {
+                queryBuilder.append(" ORDER BY");

-                case VERSIONVIEWCOUNTER:
- queryBuilder.append(" pv.view_counter DESC, p.name ASC");
-                    break;
+                switch (search.getSortOrdering()) {
+
+                    case VERSIONVIEWCOUNTER:
+ queryBuilder.append(" pv.view_counter DESC, p.name ASC");
+                        break;

-                case VERSIONCREATETIMESTAMP:
-                    queryBuilder.append(" pv.create_timestamp DESC");
-                    break;
+                    case VERSIONCREATETIMESTAMP:
+                        queryBuilder.append(" pv.create_timestamp DESC");
+                        break;

-                case NAME:
-                    queryBuilder.append(" p.name ASC");
-                    break;
+                    case NAME:
+                        queryBuilder.append(" p.name ASC");
+                        break;

-                default:
- throw new IllegalStateException("unhandled sort ordering; " + search.getSortOrdering());
+                    default:
+ throw new IllegalStateException("unhandled sort ordering; " + search.getSortOrdering());

+                }
             }

             queryBuilder.append(" LIMIT ?");
@@ -242,8 +271,13 @@
                 PkgVersion.class,
ExpressionFactory.inDbExp(PkgVersion.ID_PK_COLUMN, pkgVersionIds));

-        PrefetchTreeNode prefetchTreeNode = new PrefetchTreeNode();
+        if(null==prefetchTreeNode) {
+            prefetchTreeNode = new PrefetchTreeNode();
+        }
+
+        // we always want to get the package for a given version
         prefetchTreeNode.addPath(PkgVersion.PKG_PROPERTY);
+
         query.setPrefetchTree(prefetchTreeNode);

         List<PkgVersion> pkgVersions = context.performQuery(query);
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/PkgSearchSpecification.java Thu Apr 3 10:08:30 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/PkgSearchSpecification.java Wed Apr 9 11:01:12 2014 UTC
@@ -9,6 +9,8 @@
 import org.haikuos.haikudepotserver.dataobjects.PkgCategory;
 import org.haikuos.haikudepotserver.support.AbstractSearchSpecification;

+import java.util.List;
+
 /**
* <p>This model object specifies the parameters of a search into the system for packages. See the * {@link org.haikuos.haikudepotserver.pkg.PkgOrchestrationService} for further detail on this.</p>
@@ -21,6 +23,8 @@
         VERSIONCREATETIMESTAMP,
         VERSIONVIEWCOUNTER
     }
+
+    private List<String> pkgNames;

     private Architecture architecture;

@@ -30,6 +34,14 @@

     private SortOrdering sortOrdering;

+    public List<String> getPkgNames() {
+        return pkgNames;
+    }
+
+    public void setPkgNames(List<String> pkgNames) {
+        this.pkgNames = pkgNames;
+    }
+
     public Architecture getArchitecture() {
         return architecture;
     }
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java Wed Apr 9 11:01:12 2014 UTC
@@ -58,7 +58,7 @@
         }
     }

- private void addPkgIcon(ObjectContext objectContext, Pkg pkg, int size) { + private void addPngPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
         InputStream inputStream = null;

         try {
@@ -77,10 +77,31 @@
             Closeables.closeQuietly(inputStream);
         }
     }
+
+    private void addHvifPkgIcon(ObjectContext objectContext, Pkg pkg) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = this.getClass().getResourceAsStream("/sample.hvif");
+            pkgService.storePkgIconImage(
+                    inputStream,
+ MediaType.getByCode(objectContext, MediaType.MEDIATYPE_HAIKUVECTORICONFILE).get(),
+                    null,
+                    objectContext,
+                    pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading an icon",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }

     private void addPkgIcons(ObjectContext objectContext, Pkg pkg) {
-        addPkgIcon(objectContext, pkg, 16);
-        addPkgIcon(objectContext, pkg, 32);
+        addPngPkgIcon(objectContext, pkg, 16);
+        addPngPkgIcon(objectContext, pkg, 32);
+        addHvifPkgIcon(objectContext, pkg);
     }

     public StandardTestData createStandardTestData() {
@@ -103,19 +124,25 @@
         result.pkg1.setActive(true);
         result.pkg1.setName("pkg1");

+        {
+ PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); + result.pkg1.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GRAPHICS").get());
+        }
+
         addPkgScreenshot(context,result.pkg1);
         addPkgScreenshot(context,result.pkg1);
         addPkgScreenshot(context,result.pkg1);
         addPkgIcons(context, result.pkg1);

-        result.pkg1Version1 = context.newObject(PkgVersion.class);
-        result.pkg1Version1.setActive(Boolean.FALSE);
-        result.pkg1Version1.setArchitecture(x86);
-        result.pkg1Version1.setMajor("1");
-        result.pkg1Version1.setMicro("2");
-        result.pkg1Version1.setRevision(3);
-        result.pkg1Version1.setPkg(result.pkg1);
-        result.pkg1Version1.setRepository(result.repository);
+        result.pkg1Version1x86 = context.newObject(PkgVersion.class);
+        result.pkg1Version1x86.setActive(Boolean.FALSE);
+        result.pkg1Version1x86.setArchitecture(x86);
+        result.pkg1Version1x86.setMajor("1");
+        result.pkg1Version1x86.setMicro("2");
+        result.pkg1Version1x86.setRevision(3);
+        result.pkg1Version1x86.setPkg(result.pkg1);
+        result.pkg1Version1x86.setRepository(result.repository);

         result.pkg1Version2x86 = context.newObject(PkgVersion.class);
         result.pkg1Version2x86.setActive(Boolean.TRUE);
@@ -203,7 +230,7 @@
         public Repository repository;

         public Pkg pkg1;
-        public PkgVersion pkg1Version1;
+        public PkgVersion pkg1Version1x86;
         public PkgVersion pkg1Version2x86;
         public PkgVersion pkg1Version2x86_gcc2;

=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Wed Apr 9 11:01:12 2014 UTC
@@ -11,6 +11,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.net.MediaType;
 import com.googlecode.jsonrpc4j.Base64;
 import junit.framework.Assert;
@@ -19,8 +20,10 @@
 import org.haikuos.haikudepotserver.api1.PkgApi;
 import org.haikuos.haikudepotserver.api1.model.pkg.*;
 import org.haikuos.haikudepotserver.api1.support.BadPkgIconException;
+import org.haikuos.haikudepotserver.api1.support.LimitExceededException;
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
 import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
 import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
 import org.haikuos.haikudepotsever.AbstractIntegrationTest;
 import org.haikuos.haikudepotsever.IntegrationTestSupportService;
@@ -131,7 +134,7 @@
         GetPkgRequest request = new GetPkgRequest();
         request.architectureCode = "x86";
         request.name = "pkg1";
-        request.versionType = GetPkgRequest.VersionType.LATEST;
+        request.versionType = PkgVersionType.LATEST;
         request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;

         // ------------------------------------
@@ -156,7 +159,7 @@
         GetPkgRequest request = new GetPkgRequest();
         request.architectureCode = "x86";
         request.name = "pkg9";
-        request.versionType = GetPkgRequest.VersionType.LATEST;
+        request.versionType = PkgVersionType.LATEST;
         request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;

         try {
@@ -185,7 +188,7 @@
GetPkgIconsResult result = pkgApi.getPkgIcons(new GetPkgIconsRequest("pkg1"));
         // ------------------------------------

-        Assertions.assertThat(result.pkgIcons.size()).isEqualTo(2);
+        Assertions.assertThat(result.pkgIcons.size()).isEqualTo(3);
         // check more stuff...

     }
@@ -289,13 +292,13 @@

Assertions.assertThat(pkgOptionalafter.get().getPkgIcons().size()).isEqualTo(3);

- Optional<PkgIcon> pkgIcon16Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 16); + Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon16Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 16); Assertions.assertThat(pkgIcon16Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample16);

- Optional<PkgIcon> pkgIcon32Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 32); + Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon32Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 32); Assertions.assertThat(pkgIcon32Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample32);

- Optional<PkgIcon> pkgIconHvifOptional = pkgOptionalafter.get().getPkgIcon(mediaTypeHvif, null); + Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIconHvifOptional = pkgOptionalafter.get().getPkgIcon(mediaTypeHvif, null); Assertions.assertThat(pkgIconHvifOptional.get().getPkgIconImage().get().getData()).isEqualTo(sampleHvif);
         }
     }
@@ -314,7 +317,7 @@
         {
             ObjectContext objectContext = serverRuntime.getContext();
Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); - Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(2); // 16 and 32 px sizes + Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(3); // 16 and 32 px sizes + hvif
         }

         // ------------------------------------
@@ -345,7 +348,7 @@

         for(int i=0;i<sortedScreenshots.size();i++) {
             PkgScreenshot pkgScreenshot = sortedScreenshots.get(i);
- GetPkgScreenshotsResult.PkgScreenshot apiPkgScreenshot = result.items.get(i); + org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot apiPkgScreenshot = result.items.get(i); Assertions.assertThat(pkgScreenshot.getCode()).isEqualTo(apiPkgScreenshot.code);
             Assertions.assertThat(pkgScreenshot.getWidth()).isEqualTo(320);
Assertions.assertThat(pkgScreenshot.getHeight()).isEqualTo(240);
@@ -526,5 +529,103 @@
Assertions.assertThat(result.pkgVersionLocalizations.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); Assertions.assertThat(result.pkgVersionLocalizations.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
     }
+
+    /**
+ * <p>This test is just checking that if too many packages are requested that it throws the right
+     * sort of exception.</p>
+     */
+
+    @Test
+    public void testGetBulkPkg__limitExceeded() throws Exception {
+
+        GetBulkPkgRequest request = new GetBulkPkgRequest();
+ request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
+        request.versionType = PkgVersionType.LATEST;
+        request.architectureCode = "x86";
+        request.naturalLanguageCode = "en";
+        request.pkgNames = Lists.newArrayList();
+
+        while(request.pkgNames.size() < PkgApi.GETBULKPKG_LIMIT + 1) {
+            request.pkgNames.add("pkg");
+        }
+
+        try {
+            // ------------------------------------
+            pkgApi.getBulkPkg(request);
+            // ------------------------------------
+ Assert.fail("expected an instance of "+ LimitExceededException.class.getSimpleName()+" to be thrown");
+        }
+        catch(LimitExceededException lee) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testGetBulkPkg__ok() throws Exception {
+        integrationTestSupportService.createStandardTestData();
+
+        GetBulkPkgRequest request = new GetBulkPkgRequest();
+ request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
+        request.versionType = PkgVersionType.LATEST;
+        request.architectureCode = "x86";
+        request.naturalLanguageCode = "en";
+        request.pkgNames = ImmutableList.of("pkg1","pkg2","pkg3");
+
+        // ------------------------------------
+        GetBulkPkgResult result = pkgApi.getBulkPkg(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.pkgs.size()).isEqualTo(3);
+
+        // now check pkg1 because it has some in-depth data on it.
+
+ GetBulkPkgResult.Pkg pkg1 = Iterables.tryFind(result.pkgs, new Predicate<GetBulkPkgResult.Pkg>() {
+            @Override
+            public boolean apply(GetBulkPkgResult.Pkg input) {
+                return input.name.equals("pkg1");
+            }
+        }).get();
+
+        Assertions.assertThat(pkg1.name).isEqualTo("pkg1");
+        Assertions.assertThat(pkg1.modifyTimestamp).isNotNull();
+
+        Assertions.assertThat(pkg1.pkgCategoryCodes.size()).isEqualTo(1);
+ Assertions.assertThat(pkg1.pkgCategoryCodes.get(0)).isEqualTo("GRAPHICS");
+
+        Assertions.assertThat(pkg1.userRatingAverage).isNotNull();
+ Assertions.assertThat(pkg1.userRatingAverage).isGreaterThanOrEqualTo(0.0f); + Assertions.assertThat(pkg1.userRatingAverage).isLessThanOrEqualTo(5.0f);
+
+ // there are three screen-shots loaded, but they are all the same so we can just check that the first
+        // one is correct.
+        Assertions.assertThat(pkg1.pkgScreenshots.size()).isEqualTo(3);
+        Assertions.assertThat(pkg1.pkgScreenshots.get(0).code).isNotNull();
+ Assertions.assertThat(pkg1.pkgScreenshots.get(0).width).isEqualTo(320); + Assertions.assertThat(pkg1.pkgScreenshots.get(0).height).isEqualTo(240);
+
+ // basic check here to make sure that the HPKR data is able to be flagged as being there.
+        Assertions.assertThat(pkg1.pkgIcons.size()).isEqualTo(3);
+        Assertions.assertThat(
+ Iterables.tryFind(pkg1.pkgIcons, new Predicate<org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
+            @Override
+ public boolean apply(org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon input) { + return input.mediaTypeCode.equals(org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE);
+            }
+        }).isPresent()).isTrue();
+
+        Assertions.assertThat(pkg1.versions.size()).isEqualTo(1);
+ Assertions.assertThat(pkg1.versions.get(0).naturalLanguageCode).isEqualTo("en"); + Assertions.assertThat(pkg1.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(pkg1.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish"); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isNotNull(); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isGreaterThanOrEqualTo(0.0f); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isLessThanOrEqualTo(5.0f);
+        Assertions.assertThat(pkg1.versions.get(0).major).isEqualTo("1");
+        Assertions.assertThat(pkg1.versions.get(0).micro).isEqualTo("2");
+        Assertions.assertThat(pkg1.versions.get(0).revision).isEqualTo(4);
+        Assertions.assertThat(pkg1.versions.get(0).preRelease).isNull();
+        Assertions.assertThat(pkg1.versions.get(0).minor).isNull();
+
+    }

 }

==============================================================================
Revision: b4e575bb22f3
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Wed Apr  9 11:01:59 2014 UTC
Log:      + correct spelling mistake in packaging

http://code.google.com/p/haiku-depot-web-app/source/detail?r=b4e575bb22f3

Added:
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/AbstractIntegrationTest.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/CaptchaApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/RepositoryApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/UserApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationServiceIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/repository/RepositoryImportServiceIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/support/ImageHelperTest.java
Deleted:
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/AbstractIntegrationTest.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/CaptchaApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/MiscelaneousApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/RepositoryApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/UserApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/PkgOrchestrationServiceIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/repository/RepositoryImportServiceIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/support/ImageHelperTest.java
Modified:
 /haikudepotserver-webapp/src/test/resources/spring/test.xml

=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/AbstractIntegrationTest.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver;
+
+import com.google.common.base.Optional;
+import com.google.common.io.ByteStreams;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.AuthenticationHelper;
+import org.haikuos.haikudepotserver.security.AuthenticationService;
+import org.haikuos.haikudepotserver.support.Closeables;
+import org.haikuos.haikudepotserver.support.db.migration.ManagedDatabase;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * <p>This superclass of all of the tests has a hook to run before each integration test. The hook will + * basically delete all of the schema objects and then prompt the database schema migration to again, + * repopulate the database afresh. This ensures that the database is taken from a blank state at the + * start of each test. This is important for tests to maintain their independence. The use a + * 'transaction' over the test is not possible here as the ORM technology isnot bound to a single
+ * transaction.</p>
+ */
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration({
+        "classpath:/spring/servlet-context.xml",
+        "classpath:/spring/application-context.xml",
+        "classpath:/spring/test.xml"
+})
+public abstract class AbstractIntegrationTest {
+
+ protected static Logger logger = LoggerFactory.getLogger(AbstractIntegrationTest.class);
+
+ private final static String DATABASEPRODUCTNAME_POSTGRES = "PostgreSQL";
+
+    @Resource
+    ApplicationContext applicationContext;
+
+    @Resource
+    protected ServerRuntime serverRuntime;
+
+    @Resource
+    AuthenticationService authenticationService;
+
+    protected byte[] getResourceData(String path) throws IOException {
+        InputStream inputStream = null;
+
+        try {
+            inputStream = this.getClass().getResourceAsStream(path);
+
+            if(null==inputStream) {
+ throw new IllegalStateException("unable to find the test resource; "+path);
+            }
+
+            return ByteStreams.toByteArray(inputStream);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+    /**
+ * <p>Before each test is run, we want to remove all of the database objects and then re-populate
+     * them back again.</p>
+     */
+
+    @Before
+    public void beforeEachTest() {
+
+        logger.info("will prepare for the next test");
+
+        // reset the apache cayenne cache before we go behind its back and
+        // clear out the database for the next test.
+
+        serverRuntime.getDataDomain().getQueryCache().clear();
+        serverRuntime.getDataDomain().getSharedSnapshotCache().clear();
+        logger.info("prep; have cleared out cayenne caches");
+
+        // get all of the databases that are managed.
+
+ Map<String,ManagedDatabase> managedDatabases = applicationContext.getBeansOfType(ManagedDatabase.class);
+
+        for(ManagedDatabase managedDatabase : managedDatabases.values()) {
+
+            Connection connection = null;
+            PreparedStatement statement = null;
+
+            try {
+ connection = managedDatabase.getDataSource().getConnection();
+                connection.setAutoCommit(false);
+
+ String databaseProductName = connection.getMetaData().getDatabaseProductName();
+
+ if(!databaseProductName.equals(DATABASEPRODUCTNAME_POSTGRES)) {
+                    throw new IllegalStateException(String.format(
+ "the system is designed to be tested against %s database product, but is '%s'",
+                            DATABASEPRODUCTNAME_POSTGRES,
+                            databaseProductName));
+                }
+
+                {
+ String statementString = "DROP SCHEMA "+managedDatabase.getSchema()+" CASCADE"; + statement = connection.prepareStatement(statementString);
+                    statement.execute();
+                }
+            }
+            catch(SQLException se) {
+ throw new IllegalStateException("a database problem has arisen in preparing for an integration test",se);
+            }
+            finally {
+                Closeables.closeQuietly(statement);
+                Closeables.closeQuietly(connection);
+            }
+
+            managedDatabase.migrate();
+ logger.info("prep; did drop database objects for schema '{}' and re-create them", managedDatabase.getSchema());
+        }
+
+
+        logger.info("did prepare for the next test");
+    }
+
+    protected void setAuthenticatedUser(String nickname) {
+        ObjectContext objectContext = serverRuntime.getContext();
+ Optional<User> rootUser = User.getByNickname(objectContext, nickname); + AuthenticationHelper.setAuthenticatedUserObjectId(Optional.of(rootUser.get().getObjectId()));
+    }
+
+    /**
+ * <p>Some tests will enforce authorization; in order to test the method's actual logic rather than the + * authorization logic, it is sometimes handy to force the authenticated user to be root. This method
+     * will do this.</p>
+     */
+
+    protected void setAuthenticatedUserToRoot() {
+        setAuthenticatedUser("root");
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/IntegrationTestSupportService.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.MediaType;
+import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
+import org.haikuos.haikudepotserver.support.Closeables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.InputStream;
+
+/**
+ * <p>This class is designed to help out with creating some common test data that can be re-used between tests.</p>
+ */
+
+@Service
+public class IntegrationTestSupportService {
+
+ protected static Logger logger = LoggerFactory.getLogger(IntegrationTestSupportService.class);
+
+    @Resource
+    ServerRuntime serverRuntime;
+
+    @Resource
+    PkgOrchestrationService pkgService;
+
+    private ObjectContext objectContext = null;
+
+    public ObjectContext getObjectContext() {
+        if(null==objectContext) {
+            objectContext = serverRuntime.getContext();
+        }
+
+        return objectContext;
+    }
+
+ private PkgScreenshot addPkgScreenshot(ObjectContext objectContext, Pkg pkg) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = IntegrationTestSupportService.class.getResourceAsStream("/sample-320x240.png"); + return pkgService.storePkgScreenshotImage(inputStream, objectContext, pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading a sample screenshot into a test package",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+ private void addPngPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = this.getClass().getResourceAsStream(String.format("/sample-%dx%d.png",size,size));
+            pkgService.storePkgIconImage(
+                    inputStream,
+ MediaType.getByCode(objectContext, com.google.common.net.MediaType.PNG.toString()).get(),
+                    size,
+                    objectContext,
+                    pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading an icon",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+    private void addHvifPkgIcon(ObjectContext objectContext, Pkg pkg) {
+        InputStream inputStream = null;
+
+        try {
+ inputStream = this.getClass().getResourceAsStream("/sample.hvif");
+            pkgService.storePkgIconImage(
+                    inputStream,
+ MediaType.getByCode(objectContext, MediaType.MEDIATYPE_HAIKUVECTORICONFILE).get(),
+                    null,
+                    objectContext,
+                    pkg);
+        }
+        catch(Exception e) {
+ throw new IllegalStateException("an issue has arisen loading an icon",e);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+    private void addPkgIcons(ObjectContext objectContext, Pkg pkg) {
+        addPngPkgIcon(objectContext, pkg, 16);
+        addPngPkgIcon(objectContext, pkg, 32);
+        addHvifPkgIcon(objectContext, pkg);
+    }
+
+    public StandardTestData createStandardTestData() {
+
+        logger.info("will create standard test data");
+
+        ObjectContext context = getObjectContext();
+        StandardTestData result = new StandardTestData();
+
+        Architecture x86 = Architecture.getByCode(context, "x86").get();
+ Architecture x86_gcc2 = Architecture.getByCode(context, "x86_gcc2").get();
+
+        result.repository = context.newObject(Repository.class);
+        result.repository.setActive(Boolean.TRUE);
+        result.repository.setCode("testrepository");
+        result.repository.setArchitecture(x86);
+        result.repository.setUrl("file:///");
+
+        result.pkg1 = context.newObject(Pkg.class);
+        result.pkg1.setActive(true);
+        result.pkg1.setName("pkg1");
+
+        {
+ PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); + result.pkg1.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GRAPHICS").get());
+        }
+
+        addPkgScreenshot(context,result.pkg1);
+        addPkgScreenshot(context,result.pkg1);
+        addPkgScreenshot(context,result.pkg1);
+        addPkgIcons(context, result.pkg1);
+
+        result.pkg1Version1x86 = context.newObject(PkgVersion.class);
+        result.pkg1Version1x86.setActive(Boolean.FALSE);
+        result.pkg1Version1x86.setArchitecture(x86);
+        result.pkg1Version1x86.setMajor("1");
+        result.pkg1Version1x86.setMicro("2");
+        result.pkg1Version1x86.setRevision(3);
+        result.pkg1Version1x86.setPkg(result.pkg1);
+        result.pkg1Version1x86.setRepository(result.repository);
+
+        result.pkg1Version2x86 = context.newObject(PkgVersion.class);
+        result.pkg1Version2x86.setActive(Boolean.TRUE);
+        result.pkg1Version2x86.setArchitecture(x86);
+        result.pkg1Version2x86.setMajor("1");
+        result.pkg1Version2x86.setMicro("2");
+        result.pkg1Version2x86.setRevision(4);
+        result.pkg1Version2x86.setPkg(result.pkg1);
+        result.pkg1Version2x86.setRepository(result.repository);
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); + pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); + result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_SPANISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionSpanish"); + pkgVersionLocalization.setSummary("pkg1Version2SummarySpanish"); + result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        result.pkg1Version2x86_gcc2 = context.newObject(PkgVersion.class);
+        result.pkg1Version2x86_gcc2.setActive(Boolean.TRUE);
+        result.pkg1Version2x86_gcc2.setArchitecture(x86_gcc2);
+        result.pkg1Version2x86_gcc2.setMajor("1");
+        result.pkg1Version2x86_gcc2.setMicro("2");
+        result.pkg1Version2x86_gcc2.setRevision(4);
+        result.pkg1Version2x86_gcc2.setPkg(result.pkg1);
+        result.pkg1Version2x86_gcc2.setRepository(result.repository);
+
+ // this is the same as the x86 version so that comparisons with English will happen.
+
+        {
+ PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); + pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); + pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); + result.pkg1Version2x86_gcc2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+        }
+
+        result.pkg2 = context.newObject(Pkg.class);
+        result.pkg2.setActive(true);
+        result.pkg2.setName("pkg2");
+
+        result.pkg2Version1 = context.newObject(PkgVersion.class);
+        result.pkg2Version1.setActive(Boolean.TRUE);
+        result.pkg2Version1.setArchitecture(x86);
+        result.pkg2Version1.setMajor("1");
+        result.pkg2Version1.setMicro("2");
+        result.pkg2Version1.setRevision(3);
+        result.pkg2Version1.setPkg(result.pkg2);
+        result.pkg2Version1.setRepository(result.repository);
+
+        result.pkg3 = context.newObject(Pkg.class);
+        result.pkg3.setActive(true);
+        result.pkg3.setName("pkg3");
+
+        result.pkg3Version1 = context.newObject(PkgVersion.class);
+        result.pkg3Version1.setActive(Boolean.TRUE);
+        result.pkg3Version1.setArchitecture(x86);
+        result.pkg3Version1.setMajor("1");
+        result.pkg3Version1.setMicro("2");
+        result.pkg3Version1.setRevision(3);
+        result.pkg3Version1.setPkg(result.pkg3);
+        result.pkg3Version1.setRepository(result.repository);
+
+        context.commitChanges();
+
+        logger.info("did create standard test data");
+
+        return result;
+    }
+
+    /**
+ * <p>This class is a container that carries some basic test-case data.</p>
+     */
+
+    public static class StandardTestData {
+
+        public Repository repository;
+
+        public Pkg pkg1;
+        public PkgVersion pkg1Version1x86;
+        public PkgVersion pkg1Version2x86;
+        public PkgVersion pkg1Version2x86_gcc2;
+
+        public Pkg pkg2;
+        public PkgVersion pkg2Version1;
+
+        public Pkg pkg3;
+        public PkgVersion pkg3Version1;
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/CaptchaApiIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaRequest; +import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaResult;
+import org.haikuos.haikudepotserver.captcha.model.CaptchaRepository;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+
+public class CaptchaApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    CaptchaApi captchaApi;
+
+    @Resource
+    CaptchaRepository captchaRepository;
+
+    /**
+ * <p>This is a bit of a limited test because there is nothing to actually check without having a person + * to interpret the image. In any case, it will provide for an element of just exercising the API to make
+     * sure that it at least does not fail.</p>
+     */
+
+    @Test
+    public void testGenerateCaptcha() {
+
+        // ------------------------------------
+
+ GenerateCaptchaResult result = captchaApi.generateCaptcha(new GenerateCaptchaRequest());
+
+        // ------------------------------------
+
+        Assertions.assertThat(result.token).isNotEmpty();
+ Assertions.assertThat(captchaRepository.get(result.token)).isNotNull();
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.api1.model.AuthorizationTargetType;
+import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
+import org.haikuos.haikudepotserver.dataobjects.PkgCategory;
+import org.haikuos.haikudepotserver.security.model.Permission;
+import org.haikuos.haikudepotserver.api1.model.miscellaneous.*;
+import org.haikuos.haikudepotserver.support.RuntimeInformationService;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+public class MiscelaneousApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    MiscellaneousApi miscellaneousApi;
+
+    @Resource
+    RuntimeInformationService runtimeInformationService;
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    private void assertTargetAndPermission(
+            IntegrationTestSupportService.StandardTestData data,
+ CheckAuthorizationResult.AuthorizationTargetAndPermission targetAndPermission,
+            boolean result) {
+ Assertions.assertThat(targetAndPermission.permissionCode).isEqualTo(Permission.PKG_EDITICON.name()); + Assertions.assertThat(targetAndPermission.targetIdentifier).isEqualTo(data.pkg1.getName()); + Assertions.assertThat(targetAndPermission.targetType).isEqualTo(AuthorizationTargetType.PKG); + Assertions.assertThat(targetAndPermission.authorized).isEqualTo(result);
+    }
+
+    @Test
+    public void testGetAllPkgCategories() {
+
+        // ------------------------------------
+ GetAllPkgCategoriesResult result = miscellaneousApi.getAllPkgCategories(new GetAllPkgCategoriesRequest());
+        // ------------------------------------
+
+        ObjectContext objectContext = serverRuntime.getContext();
+
+ List<PkgCategory> pkgCategories = PkgCategory.getAll(objectContext);
+
+ Assertions.assertThat(pkgCategories.size()).isEqualTo(result.pkgCategories.size());
+
+        for(int i=0;i<pkgCategories.size();i++) {
+            PkgCategory pkgCategory = pkgCategories.get(i);
+ GetAllPkgCategoriesResult.PkgCategory apiPkgCategory = result.pkgCategories.get(i); + Assertions.assertThat(pkgCategory.getName()).isEqualTo(apiPkgCategory.name); + Assertions.assertThat(pkgCategory.getCode()).isEqualTo(apiPkgCategory.code);
+        }
+    }
+
+    @Test
+    public void testGetAllNaturalLanguages() {
+
+        // ------------------------------------
+ GetAllNaturalLanguagesResult result = miscellaneousApi.getAllNaturalLanguages(new GetAllNaturalLanguagesRequest());
+        // ------------------------------------
+
+        ObjectContext objectContext = serverRuntime.getContext();
+
+ List<NaturalLanguage> naturalLanguages = NaturalLanguage.getAll(objectContext);
+
+ Assertions.assertThat(naturalLanguages.size()).isEqualTo(result.naturalLanguages.size());
+
+        for(int i=0;i<naturalLanguages.size();i++) {
+            NaturalLanguage naturalLanguage = naturalLanguages.get(i);
+ GetAllNaturalLanguagesResult.NaturalLanguage apiNaturalLanguage = result.naturalLanguages.get(i); + Assertions.assertThat(naturalLanguage.getName()).isEqualTo(apiNaturalLanguage.name); + Assertions.assertThat(naturalLanguage.getCode()).isEqualTo(apiNaturalLanguage.code);
+        }
+    }
+
+    @Test
+    public void checkAuthorizationRequest_asUnauthenticated() {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                data.pkg1.getName(),
+                Permission.PKG_EDITICON.name()));
+
+        // ------------------------------------
+ CheckAuthorizationResult result = miscellaneousApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + assertTargetAndPermission(data, result.targetAndPermissions.get(0), false);
+
+    }
+
+ // TODO : when some more sophisticated cases are available; implement some better tests
+    @Test
+    public void checkAuthorizationRequest_asRoot() {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+        setAuthenticatedUserToRoot();
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                data.pkg1.getName(),
+                Permission.PKG_EDITICON.name()));
+
+        // ------------------------------------
+ CheckAuthorizationResult result = miscellaneousApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + assertTargetAndPermission(data, result.targetAndPermissions.get(0), true);
+
+    }
+
+    @Test
+    public void getRuntimeInformation_asUnauthenticated() {
+
+        // ------------------------------------
+ GetRuntimeInformationResult result = miscellaneousApi.getRuntimeInformation(new GetRuntimeInformationRequest());
+        // ------------------------------------
+
+        Assertions.assertThat(result.javaVersion).isNull();
+    }
+
+    @Test
+    public void getRuntimeInformation_asRoot() {
+
+        setAuthenticatedUserToRoot();
+
+        // ------------------------------------
+ GetRuntimeInformationResult result = miscellaneousApi.getRuntimeInformation(new GetRuntimeInformationRequest());
+        // ------------------------------------
+
+ Assertions.assertThat(result.javaVersion).isEqualTo(runtimeInformationService.getJavaVersion());
+
+    }
+
+    @Test
+    public void testGetAllMessages() throws Exception {
+
+        // ------------------------------------
+ GetAllMessagesResult result = miscellaneousApi.getAllMessages(new GetAllMessagesRequest(NaturalLanguage.CODE_ENGLISH));
+        // ------------------------------------
+
+ Assertions.assertThat(result.messages.get("test.it")).isEqualTo("Test line for integration testing");
+
+    }
+
+    private Optional<GetAllArchitecturesResult.Architecture> isPresent(
+            GetAllArchitecturesResult result,
+            final String architectureCode) {
+ return Iterables.tryFind(result.architectures, new Predicate<GetAllArchitecturesResult.Architecture>() {
+            @Override
+ public boolean apply(GetAllArchitecturesResult.Architecture architecture) {
+                return architecture.code.equals(architectureCode);
+            }
+        });
+    }
+
+    @Test
+    public void testGetAllArchitectures() {
+
+        // ------------------------------------
+ GetAllArchitecturesResult result = miscellaneousApi.getAllArchitectures(new GetAllArchitecturesRequest());
+        // ------------------------------------
+
+        // not sure what architectures there may be in the future, but
+        // we will just check for a couple that we know to be there.
+
+ Assertions.assertThat(isPresent(result,"x86").isPresent()).isTrue(); + Assertions.assertThat(isPresent(result,"x86_gcc2").isPresent()).isTrue(); + Assertions.assertThat(isPresent(result,"mips").isPresent()).isFalse();
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/PkgApiIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.net.MediaType;
+import com.googlecode.jsonrpc4j.Base64;
+import junit.framework.Assert;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.api1.model.pkg.*;
+import org.haikuos.haikudepotserver.api1.support.BadPkgIconException;
+import org.haikuos.haikudepotserver.api1.support.LimitExceededException;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
+import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.List;
+
+public class PkgApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    @Resource
+    PkgApi pkgApi;
+
+    @Resource
+    PkgOrchestrationService pkgService;
+
+    @Test
+    public void testUpdatePkgCategories() throws Exception {
+
+        setAuthenticatedUserToRoot();
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+        // setup some categories as a start condition.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+            Pkg pkg = Pkg.getByName(context, data.pkg1.getName()).get();
+
+            {
+ PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GAMES").get()); + pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
+            }
+
+            {
+ PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "BUSINESS").get()); + pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
+            }
+
+            context.commitChanges();
+        }
+
+ UpdatePkgCategoriesRequest request = new UpdatePkgCategoriesRequest();
+        request.pkgName = data.pkg1.getName();
+ request.pkgCategoryCodes = ImmutableList.of("BUSINESS", "DEVELOPMENT");
+
+        // ------------------------------------
+        pkgApi.updatePkgCategories(request);
+        // ------------------------------------
+
+ // now we need to check on those categories. GAMES should have gone, BUSINESS should remain
+        // and DEVELOPMENT should be added.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+            Pkg pkg = Pkg.getByName(context, data.pkg1.getName()).get();
+
+ Assertions.assertThat(ImmutableSet.of("BUSINESS", "DEVELOPMENT")).isEqualTo(
+                ImmutableSet.copyOf(Iterables.transform(
+                        pkg.getPkgPkgCategories(),
+                        new Function<PkgPkgCategory, String>() {
+                            @Override
+                            public String apply(PkgPkgCategory input) {
+                                return input.getPkgCategory().getCode();
+                            }
+                        }
+                ))
+            );
+        }
+
+    }
+
+    @Test
+    public void searchPkgsTest() {
+        integrationTestSupportService.createStandardTestData();
+
+        SearchPkgsRequest request = new SearchPkgsRequest();
+        request.architectureCode = "x86";
+        request.expression = "pk";
+        request.expressionType = SearchPkgsRequest.ExpressionType.CONTAINS;
+        request.limit = 2;
+        request.offset = 0;
+
+        // ------------------------------------
+        SearchPkgsResult result = pkgApi.searchPkgs(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.hasMore).isTrue();
+        Assertions.assertThat(result.items.size()).isEqualTo(2);
+        Assertions.assertThat(result.items.get(0).name).isEqualTo("pkg1");
+        Assertions.assertThat(result.items.get(1).name).isEqualTo("pkg2");
+    }
+
+    /**
+ * <p>In this test, an German localization is requested, but there is no localization present for German so it will
+     * fall back English.</p>
+     */
+
+    @Test
+    public void testGetPkg_found() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+
+        GetPkgRequest request = new GetPkgRequest();
+        request.architectureCode = "x86";
+        request.name = "pkg1";
+        request.versionType = PkgVersionType.LATEST;
+        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
+
+        // ------------------------------------
+        GetPkgResult result = pkgApi.getPkg(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.name).isEqualTo("pkg1");
+        Assertions.assertThat(result.versions.size()).isEqualTo(1);
+ Assertions.assertThat(result.versions.get(0).architectureCode).isEqualTo("x86");
+        Assertions.assertThat(result.versions.get(0).major).isEqualTo("1");
+        Assertions.assertThat(result.versions.get(0).micro).isEqualTo("2");
+ Assertions.assertThat(result.versions.get(0).revision).isEqualTo(4); + Assertions.assertThat(result.versions.get(0).naturalLanguageCode).isEqualTo(NaturalLanguage.CODE_ENGLISH); + Assertions.assertThat(result.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(result.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
+    }
+
+    @Test
+    public void testGetPkg_notFound() {
+        integrationTestSupportService.createStandardTestData();
+
+        GetPkgRequest request = new GetPkgRequest();
+        request.architectureCode = "x86";
+        request.name = "pkg9";
+        request.versionType = PkgVersionType.LATEST;
+        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
+
+        try {
+
+            // ------------------------------------
+            pkgApi.getPkg(request);
+            // ------------------------------------
+
+ Assert.fail("expected an instance of "+ObjectNotFoundException.class.getSimpleName()+" to be thrown, but was not");
+        }
+        catch(ObjectNotFoundException onfe) {
+ Assertions.assertThat(onfe.getEntityName()).isEqualTo(Pkg.class.getSimpleName());
+            Assertions.assertThat(onfe.getIdentifier()).isEqualTo("pkg9");
+        }
+        catch(Throwable th) {
+ Assert.fail("expected an instance of "+ObjectNotFoundException.class.getSimpleName()+" to be thrown, but "+th.getClass().getSimpleName()+" was instead");
+        }
+    }
+
+    @Test
+    public void testGetPkgIcons() throws Exception {
+
+        integrationTestSupportService.createStandardTestData();
+
+        // ------------------------------------
+ GetPkgIconsResult result = pkgApi.getPkgIcons(new GetPkgIconsRequest("pkg1"));
+        // ------------------------------------
+
+        Assertions.assertThat(result.pkgIcons.size()).isEqualTo(3);
+        // check more stuff...
+
+    }
+
+    /**
+     * <p>Here we are trying to load the HVIF data in as PNG images.</p>
+     */
+
+    @Test
+    public void testConfigurePkgIcon_badData() throws Exception {
+
+        setAuthenticatedUserToRoot();
+        integrationTestSupportService.createStandardTestData();
+
+        byte[] sampleHvif = getResourceData("/sample.hvif");
+
+        ConfigurePkgIconRequest request = new ConfigurePkgIconRequest();
+
+        request.pkgName = "pkg1";
+        request.pkgIcons = ImmutableList.of(
+                new ConfigurePkgIconRequest.PkgIcon(
+                        MediaType.PNG.toString(),
+                        16,
+                        Base64.encodeBytes(sampleHvif)),
+                new ConfigurePkgIconRequest.PkgIcon(
+                        MediaType.PNG.toString(),
+                        32,
+                        Base64.encodeBytes(sampleHvif)),
+                new ConfigurePkgIconRequest.PkgIcon(
+ org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE,
+                        null,
+                        Base64.encodeBytes(sampleHvif)));
+
+        try {
+
+            // ------------------------------------
+            pkgApi.configurePkgIcon(request);
+            // ------------------------------------
+
+ Assert.fail("expected an instance of '"+BadPkgIconException.class.getSimpleName()+"' to have been thrown");
+
+        }
+        catch(BadPkgIconException bpie) {
+
+ // This is the first one that failed so we should get this come up as the exception that was thrown.
+
+            Assertions.assertThat(bpie.getSize()).isEqualTo(16);
+ Assertions.assertThat(bpie.getMediaTypeCode()).isEqualTo(MediaType.PNG.toString());
+        }
+    }
+
+
+    /**
+     * <p>This test will configure the icons for the package.</p>
+     */
+
+    @Test
+    public void testConfigurePkgIcon_ok() throws Exception {
+
+        setAuthenticatedUserToRoot();
+        integrationTestSupportService.createStandardTestData();
+
+        byte[] sample16 = getResourceData("/sample-16x16.png");
+        byte[] sample32 = getResourceData("/sample-32x32.png");
+        byte[] sampleHvif = getResourceData("/sample.hvif");
+
+        ConfigurePkgIconRequest request = new ConfigurePkgIconRequest();
+
+        request.pkgName = "pkg1";
+        request.pkgIcons = ImmutableList.of(
+                new ConfigurePkgIconRequest.PkgIcon(
+                        MediaType.PNG.toString(),
+                        16,
+                        Base64.encodeBytes(sample16)),
+                new ConfigurePkgIconRequest.PkgIcon(
+                        MediaType.PNG.toString(),
+                        32,
+                        Base64.encodeBytes(sample32)),
+                new ConfigurePkgIconRequest.PkgIcon(
+ org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE,
+                        null,
+                        Base64.encodeBytes(sampleHvif)));
+
+        // ------------------------------------
+        pkgApi.configurePkgIcon(request);
+        // ------------------------------------
+
+        {
+            ObjectContext objectContext = serverRuntime.getContext();
+ Optional<Pkg> pkgOptionalafter = Pkg.getByName(objectContext, "pkg1");
+
+            org.haikuos.haikudepotserver.dataobjects.MediaType mediaTypePng
+ = org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(
+                    objectContext,
+                    MediaType.PNG.toString()).get();
+
+ org.haikuos.haikudepotserver.dataobjects.MediaType mediaTypeHvif + = org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(
+                    objectContext,
+ org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE).get();
+
+ Assertions.assertThat(pkgOptionalafter.get().getPkgIcons().size()).isEqualTo(3);
+
+ Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon16Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 16); + Assertions.assertThat(pkgIcon16Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample16);
+
+ Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon32Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 32); + Assertions.assertThat(pkgIcon32Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample32);
+
+ Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIconHvifOptional = pkgOptionalafter.get().getPkgIcon(mediaTypeHvif, null); + Assertions.assertThat(pkgIconHvifOptional.get().getPkgIconImage().get().getData()).isEqualTo(sampleHvif);
+        }
+    }
+
+    /**
+ * <p>This test knows that an icon exists for pkg1 and then removes it.</p>
+     */
+
+    @Test
+    public void testRemoveIcon() throws Exception {
+
+        setAuthenticatedUserToRoot();
+
+        integrationTestSupportService.createStandardTestData();
+
+        {
+            ObjectContext objectContext = serverRuntime.getContext();
+ Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); + Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(3); // 16 and 32 px sizes + hvif
+        }
+
+        // ------------------------------------
+        pkgApi.removePkgIcon(new RemovePkgIconRequest("pkg1"));
+        // ------------------------------------
+
+        {
+            ObjectContext objectContext = serverRuntime.getContext();
+ Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); + Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(0);
+        }
+    }
+
+    /**
+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+     */
+
+    @Test
+    public void testGetPkgScreenshots() throws ObjectNotFoundException {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+        // ------------------------------------
+ GetPkgScreenshotsResult result = pkgApi.getPkgScreenshots(new GetPkgScreenshotsRequest(data.pkg1.getName()));
+        // ------------------------------------
+
+ Assertions.assertThat(result.items.size()).isEqualTo(data.pkg1.getPkgScreenshots().size()); + List<PkgScreenshot> sortedScreenshots = data.pkg1.getSortedPkgScreenshots();
+
+        for(int i=0;i<sortedScreenshots.size();i++) {
+            PkgScreenshot pkgScreenshot = sortedScreenshots.get(i);
+ org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot apiPkgScreenshot = result.items.get(i); + Assertions.assertThat(pkgScreenshot.getCode()).isEqualTo(apiPkgScreenshot.code);
+            Assertions.assertThat(pkgScreenshot.getWidth()).isEqualTo(320);
+ Assertions.assertThat(pkgScreenshot.getHeight()).isEqualTo(240); + Assertions.assertThat(pkgScreenshot.getLength()).isEqualTo(41296);
+        }
+    }
+
+    /**
+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+     */
+
+    @Test
+    public void testGetPkgScreenshot() throws ObjectNotFoundException {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        String code = data.pkg1.getSortedPkgScreenshots().get(0).getCode();
+
+        // ------------------------------------
+ GetPkgScreenshotResult result = pkgApi.getPkgScreenshot(new GetPkgScreenshotRequest(code));
+        // ------------------------------------
+
+        Assertions.assertThat(result.code).isEqualTo(code);
+        Assertions.assertThat(result.width).isEqualTo(320);
+        Assertions.assertThat(result.height).isEqualTo(240);
+        Assertions.assertThat(result.length).isEqualTo(41296);
+    }
+
+    /**
+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+     */
+
+    @Test
+    public void testRemovePkgScreenshot() throws Exception {
+        setAuthenticatedUserToRoot();
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); + List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
+
+        if(sortedScreenshotsBefore.size() < 2) {
+ throw new IllegalStateException("the test cannot run without more than two screenshots");
+        }
+
+        final String code1 = sortedScreenshotsBefore.get(1).getCode();
+
+        // ------------------------------------
+        pkgApi.removePkgScreenshot(new RemovePkgScreenshotRequest(code1));
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();
+
+ Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(sortedScreenshotsBefore.size()-1);
+
+ Assertions.assertThat(Iterables.tryFind(sortedScreenshotsAfter, new Predicate<PkgScreenshot>() {
+            @Override
+            public boolean apply(PkgScreenshot pkgScreenshot) {
+                return pkgScreenshot.getCode().equals(code1);
+            }
+        }).isPresent()).isFalse();
+    }
+
+    /**
+ * <p>This test assumes that the test data has a pkg1 with three screenshots associated with it.</p>
+     */
+
+    @Test
+    public void testReorderPkgScreenshots() throws Exception {
+        setAuthenticatedUserToRoot();
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); + List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
+
+        if(3 != sortedScreenshotsBefore.size()) {
+ throw new IllegalStateException("the test requires that pkg1 has three screenshots associated with it");
+        }
+
+        // ------------------------------------
+        pkgApi.reorderPkgScreenshots(new ReorderPkgScreenshotsRequest(
+                data.pkg1.getName(),
+                ImmutableList.of(
+                        sortedScreenshotsBefore.get(2).getCode(),
+                        sortedScreenshotsBefore.get(0).getCode()
+                )
+        ));
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();
+
+        Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(3);
+ Assertions.assertThat(sortedScreenshotsAfter.get(0).getCode()).isEqualTo(sortedScreenshotsBefore.get(2).getCode()); + Assertions.assertThat(sortedScreenshotsAfter.get(1).getCode()).isEqualTo(sortedScreenshotsBefore.get(0).getCode()); + Assertions.assertThat(sortedScreenshotsAfter.get(2).getCode()).isEqualTo(sortedScreenshotsBefore.get(1).getCode());
+    }
+
+ private void testUpdatePkgVersionLocalization(String naturalLanguageCode) throws Exception {
+        setAuthenticatedUserToRoot();
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+ UpdatePkgVersionLocalizationRequest request = new UpdatePkgVersionLocalizationRequest();
+        request.pkgName = data.pkg1.getName();
+        request.architectureCode = "x86";
+        request.description = "testDescription";
+        request.naturalLanguageCode = naturalLanguageCode;
+        request.summary = "testSummary";
+ request.replicateToOtherArchitecturesWithSameEnglishContent = Boolean.TRUE;
+
+        // ------------------------------------
+        pkgApi.updatePkgVersionLocalization(request);
+        // ------------------------------------
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
+                    context,
+                    pkgOptional.get(),
+ Collections.singletonList(Architecture.getByCode(context, "x86").get()));
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
+
+ Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
+        }
+
+ // check that the data is copied to other architecture. A x86_gcc2 package version is known to be
+        // present in the test data.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
+                    context,
+                    pkgOptional.get(),
+ Collections.singletonList(Architecture.getByCode(context, "x86_gcc2").get()));
+
+ Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
+
+ Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); + Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
+        }
+
+    }
+
+    @Test
+ public void testUpdatePkgVersionLocalization_existingNaturalLanguage() throws Exception {
+        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_SPANISH);
+    }
+
+    @Test
+ public void testUpdatePkgVersionLocalization_newNaturalLanguage() throws Exception {
+        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_GERMAN);
+    }
+
+    /**
+ * <p>This test requests german and english, but only english ispresent so needs to check that the output
+     * contains only the english data.</p>
+     */
+
+    @Test
+    public void testGetPkgVersionLocalizations() throws Exception {
+        setAuthenticatedUserToRoot();
+
+        integrationTestSupportService.createStandardTestData();
+
+ GetPkgVersionLocalizationsRequest request = new GetPkgVersionLocalizationsRequest();
+        request.architectureCode = "x86";
+ request.naturalLanguageCodes = ImmutableList.of(NaturalLanguage.CODE_ENGLISH, NaturalLanguage.CODE_GERMAN);
+        request.pkgName = "pkg1";
+
+        // ------------------------------------
+ GetPkgVersionLocalizationsResult result = pkgApi.getPkgVersionLocalizations(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.pkgVersionLocalizations.size()).isEqualTo(1); + Assertions.assertThat(result.pkgVersionLocalizations.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(result.pkgVersionLocalizations.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
+    }
+
+    /**
+ * <p>This test is just checking that if too many packages are requested that it throws the right
+     * sort of exception.</p>
+     */
+
+    @Test
+    public void testGetBulkPkg__limitExceeded() throws Exception {
+
+        GetBulkPkgRequest request = new GetBulkPkgRequest();
+ request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
+        request.versionType = PkgVersionType.LATEST;
+        request.architectureCode = "x86";
+        request.naturalLanguageCode = "en";
+        request.pkgNames = Lists.newArrayList();
+
+        while(request.pkgNames.size() < PkgApi.GETBULKPKG_LIMIT + 1) {
+            request.pkgNames.add("pkg");
+        }
+
+        try {
+            // ------------------------------------
+            pkgApi.getBulkPkg(request);
+            // ------------------------------------
+ Assert.fail("expected an instance of "+ LimitExceededException.class.getSimpleName()+" to be thrown");
+        }
+        catch(LimitExceededException lee) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testGetBulkPkg__ok() throws Exception {
+        integrationTestSupportService.createStandardTestData();
+
+        GetBulkPkgRequest request = new GetBulkPkgRequest();
+ request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
+        request.versionType = PkgVersionType.LATEST;
+        request.architectureCode = "x86";
+        request.naturalLanguageCode = "en";
+        request.pkgNames = ImmutableList.of("pkg1","pkg2","pkg3");
+
+        // ------------------------------------
+        GetBulkPkgResult result = pkgApi.getBulkPkg(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.pkgs.size()).isEqualTo(3);
+
+        // now check pkg1 because it has some in-depth data on it.
+
+ GetBulkPkgResult.Pkg pkg1 = Iterables.tryFind(result.pkgs, new Predicate<GetBulkPkgResult.Pkg>() {
+            @Override
+            public boolean apply(GetBulkPkgResult.Pkg input) {
+                return input.name.equals("pkg1");
+            }
+        }).get();
+
+        Assertions.assertThat(pkg1.name).isEqualTo("pkg1");
+        Assertions.assertThat(pkg1.modifyTimestamp).isNotNull();
+
+        Assertions.assertThat(pkg1.pkgCategoryCodes.size()).isEqualTo(1);
+ Assertions.assertThat(pkg1.pkgCategoryCodes.get(0)).isEqualTo("GRAPHICS");
+
+        Assertions.assertThat(pkg1.userRatingAverage).isNotNull();
+ Assertions.assertThat(pkg1.userRatingAverage).isGreaterThanOrEqualTo(0.0f); + Assertions.assertThat(pkg1.userRatingAverage).isLessThanOrEqualTo(5.0f);
+
+ // there are three screen-shots loaded, but they are all the same so we can just check that the first
+        // one is correct.
+        Assertions.assertThat(pkg1.pkgScreenshots.size()).isEqualTo(3);
+        Assertions.assertThat(pkg1.pkgScreenshots.get(0).code).isNotNull();
+ Assertions.assertThat(pkg1.pkgScreenshots.get(0).width).isEqualTo(320); + Assertions.assertThat(pkg1.pkgScreenshots.get(0).height).isEqualTo(240);
+
+ // basic check here to make sure that the HPKR data is able to be flagged as being there.
+        Assertions.assertThat(pkg1.pkgIcons.size()).isEqualTo(3);
+        Assertions.assertThat(
+ Iterables.tryFind(pkg1.pkgIcons, new Predicate<org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
+            @Override
+ public boolean apply(org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon input) { + return input.mediaTypeCode.equals(org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE);
+            }
+        }).isPresent()).isTrue();
+
+        Assertions.assertThat(pkg1.versions.size()).isEqualTo(1);
+ Assertions.assertThat(pkg1.versions.get(0).naturalLanguageCode).isEqualTo("en"); + Assertions.assertThat(pkg1.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); + Assertions.assertThat(pkg1.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish"); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isNotNull(); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isGreaterThanOrEqualTo(0.0f); + Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isLessThanOrEqualTo(5.0f);
+        Assertions.assertThat(pkg1.versions.get(0).major).isEqualTo("1");
+        Assertions.assertThat(pkg1.versions.get(0).micro).isEqualTo("2");
+        Assertions.assertThat(pkg1.versions.get(0).revision).isEqualTo(4);
+        Assertions.assertThat(pkg1.versions.get(0).preRelease).isNull();
+        Assertions.assertThat(pkg1.versions.get(0).minor).isNull();
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/RepositoryApiIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Optional;
+import junit.framework.Assert;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.api1.model.pkg.SearchPkgsRequest;
+import org.haikuos.haikudepotserver.api1.model.repository.*;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+import org.haikuos.haikudepotserver.api1.support.ValidationException;
+import org.haikuos.haikudepotserver.dataobjects.Repository;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+
+public class RepositoryApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    @Resource
+    RepositoryApi repositoryApi;
+
+    @Test
+    public void testUpdateRepository() throws ObjectNotFoundException {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+        UpdateRepositoryRequest request = new UpdateRepositoryRequest();
+        request.active = false;
+        request.code = data.repository.getCode();
+ request.filter = Collections.singletonList(UpdateRepositoryRequest.Filter.ACTIVE);
+
+        // ------------------------------------
+        repositoryApi.updateRepository(request);
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<Repository> repositoryOptional = Repository.getByCode(context, data.repository.getCode());
+        Assertions.assertThat(repositoryOptional.isPresent()).isTrue();
+ Assertions.assertThat(repositoryOptional.get().getActive()).isFalse();
+
+    }
+
+    @Test
+    public void searchPkgsTest() {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+ SearchRepositoriesRequest request = new SearchRepositoriesRequest();
+        request.expression = "test";
+        request.expressionType = SearchPkgsRequest.ExpressionType.CONTAINS;
+        request.limit = 2;
+        request.offset = 0;
+
+        // ------------------------------------
+ SearchRepositoriesResult result = repositoryApi.searchRepositories(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.hasMore).isFalse();
+        Assertions.assertThat(result.items.size()).isEqualTo(1);
+ Assertions.assertThat(result.items.get(0).code).isEqualTo("testrepository");
+    }
+
+    @Test
+    public void getRepositoryTest() throws Exception {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+        GetRepositoryRequest request = new GetRepositoryRequest();
+        request.code = "testrepository";
+
+        // ------------------------------------
+        GetRepositoryResult result = repositoryApi.getRepository(request);
+        // ------------------------------------
+
+        Assertions.assertThat(result.active).isTrue();
+        Assertions.assertThat(result.architectureCode).isEqualTo("x86");
+        Assertions.assertThat(result.url).isEqualTo("file:///");
+    }
+
+    @Test
+    public void testCreateRepository_ok() throws Exception {
+        setAuthenticatedUserToRoot();
+
+        CreateRepositoryRequest request = new CreateRepositoryRequest();
+        request.architectureCode = "x86";
+        request.code = "integrationtest";
+        request.url = "http://www.somewhere.co.nz";;
+
+        // ------------------------------------
+        repositoryApi.createRepository(request);
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<Repository> repositoryAfterOptional = Repository.getByCode(context,"integrationtest"); + Assertions.assertThat(repositoryAfterOptional.get().getActive()).isTrue(); + Assertions.assertThat(repositoryAfterOptional.get().getArchitecture().getCode()).isEqualTo("x86"); + Assertions.assertThat(repositoryAfterOptional.get().getCode()).isEqualTo("integrationtest"); + Assertions.assertThat(repositoryAfterOptional.get().getUrl()).isEqualTo("http://www.somewhere.co.nz";);
+    }
+
+    @Test
+    public void testCreateRepository_codeNotUnique() throws Exception {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+        CreateRepositoryRequest request = new CreateRepositoryRequest();
+        request.architectureCode = "x86";
+        request.code = data.repository.getCode();
+        request.url = "http://www.somewhere.co.nz";;
+
+        try {
+            // ------------------------------------
+            repositoryApi.createRepository(request);
+            // ------------------------------------
+
+ Assert.fail("the repository should not have been able to be created against an already existing repository code");
+        }
+        catch(ValidationException ve) {
+ Assertions.assertThat(ve.getValidationFailures().size()).isEqualTo(1); + Assertions.assertThat(ve.getValidationFailures().get(0).getMessage()).isEqualTo("unique"); + Assertions.assertThat(ve.getValidationFailures().get(0).getProperty()).isEqualTo(Repository.CODE_PROPERTY);
+        }
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/UserApiIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Optional;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.api1.model.user.*;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+import org.haikuos.haikudepotserver.captcha.CaptchaService;
+import org.haikuos.haikudepotserver.captcha.model.Captcha;
+import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.AuthenticationService;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+
+public class UserApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    UserApi userApi;
+
+    @Resource
+    CaptchaService captchaService;
+
+    @Resource
+    AuthenticationService authenticationService;
+
+ private User createBasicUser(ObjectContext context, String nickname, String password) {
+        User user = context.newObject(User.class);
+        user.setNickname(nickname);
+        user.setPasswordSalt(); // random
+ user.setPasswordHash(authenticationService.hashPassword(user, password)); + user.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get());
+        context.commitChanges();
+        return user;
+    }
+
+    @Test
+    public void testUpdateUser() throws Exception {
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ User user = createBasicUser(context, "testuser", "yUe4o2Nwe009"); // language is english
+            setAuthenticatedUser("testuser");
+        }
+
+        UpdateUserRequest request = new UpdateUserRequest();
+        request.nickname = "testuser";
+ request.filter = Collections.singletonList(UpdateUserRequest.Filter.NATURALLANGUAGE);
+        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
+
+        // ------------------------------------
+        userApi.updateUser(request);
+        // ------------------------------------
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+            Optional<User> user = User.getByNickname(context, "testuser");
+ Assertions.assertThat(user.get().getNaturalLanguage().getCode()).isEqualTo(NaturalLanguage.CODE_GERMAN);
+        }
+
+    }
+
+    @Test
+    public void testCreateUser() throws Exception {
+
+        Captcha captcha = captchaService.generate();
+        CreateUserRequest request = new CreateUserRequest();
+        request.captchaToken = captcha.getToken();
+        request.captchaResponse = captcha.getResponse();
+        request.nickname = "testuser";
+        request.passwordClear = "Ue4nI92Rw";
+        request.naturalLanguageCode = "en";
+
+        // ------------------------------------
+        CreateUserResult result = userApi.createUser(request);
+        // ------------------------------------
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<User> userOptional = User.getByNickname(context,"testuser");
+
+        Assertions.assertThat(userOptional.isPresent()).isTrue();
+        Assertions.assertThat(userOptional.get().getActive()).isTrue();
+        Assertions.assertThat(userOptional.get().getIsRoot()).isFalse();
+ Assertions.assertThat(userOptional.get().getNickname()).isEqualTo("testuser"); + Assertions.assertThat(userOptional.get().getNaturalLanguage().getCode()).isEqualTo("en");
+
+ Assertions.assertThat(authenticationService.authenticate("testuser","Ue4nI92Rw").get()).isEqualTo(userOptional.get().getObjectId());
+    }
+
+    @Test
+    public void testGetUser_found() throws ObjectNotFoundException {
+
+        ObjectContext context = serverRuntime.getContext();
+        User user = createBasicUser(context,"testuser","yUe4o2Nwe009");
+        setAuthenticatedUser("testuser");
+
+        // ------------------------------------
+ GetUserResult result = userApi.getUser(new GetUserRequest("testuser"));
+        // ------------------------------------
+
+        Assertions.assertThat(result.nickname).isEqualTo("testuser");
+        // more to come here in time
+
+    }
+
+    @Test
+         public void testAuthenticateUser_succcess() {
+
+        ObjectContext context = serverRuntime.getContext();
+        User user = createBasicUser(context, "testuser", "U7vqpsu6BB");
+        setAuthenticatedUser("testuser");
+
+        // ------------------------------------
+ AuthenticateUserResult result = userApi.authenticateUser(new AuthenticateUserRequest("testuser","U7vqpsu6BB"));
+        // ------------------------------------
+
+        Assertions.assertThat(result.authenticated).isTrue();
+
+    }
+
+    @Test
+    public void testAuthenticateUser_fail() {
+
+        ObjectContext context = serverRuntime.getContext();
+        User user = createBasicUser(context,"testuser","U7vqpsu6BB");
+        setAuthenticatedUser("testuser");
+
+        // ------------------------------------
+ AuthenticateUserResult result = userApi.authenticateUser(new AuthenticateUserRequest("testuser","y63j20f22"));
+        // ------------------------------------
+
+        Assertions.assertThat(result.authenticated).isFalse();
+    }
+
+    @Test
+    public void testChangePassword() throws ObjectNotFoundException {
+
+        Captcha captcha = captchaService.generate();
+        ObjectContext context = serverRuntime.getContext();
+        User user = createBasicUser(context,"testuser","U7vqpsu6BB");
+        setAuthenticatedUser("testuser");
+
+        // check that the password is correctly configured.
+ Assertions.assertThat(authenticationService.authenticate("testuser","U7vqpsu6BB").get()).isEqualTo(user.getObjectId());
+
+        // now change it.
+        ChangePasswordRequest request = new ChangePasswordRequest();
+        request.nickname = "testuser";
+        request.captchaResponse = captcha.getResponse();
+        request.captchaToken = captcha.getToken();
+        request.newPasswordClear = "8R3nlp11gX";
+        request.oldPasswordClear = "U7vqpsu6BB";
+
+        // ------------------------------------
+        userApi.changePassword(request);
+        // ------------------------------------
+
+ // now check that the old authentication no longer works and the new one does work + Assertions.assertThat(authenticationService.authenticate("testuser","U7vqpsu6BB").isPresent()).isFalse(); + Assertions.assertThat(authenticationService.authenticate("testuser","8R3nlp11gX").get()).isEqualTo(user.getObjectId());
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationServiceIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,111 @@
+package org.haikuos.haikudepotserver.pkg;
+
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.dataobjects.Architecture;
+import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
+import org.haikuos.haikudepotserver.dataobjects.PkgVersionLocalization;
+import org.haikuos.haikudepotserver.dataobjects.Repository;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.haikuos.pkg.model.Pkg;
+import org.haikuos.pkg.model.PkgArchitecture;
+import org.haikuos.pkg.model.PkgVersion;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+
+public class PkgOrchestrationServiceIT extends AbstractIntegrationTest {
+
+    @Resource
+    PkgOrchestrationService pkgOrchestrationService;
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    private void importTestPackageWithMinor(String minor) {
+        ObjectContext context = serverRuntime.getContext();
+ Repository repository = Repository.getByCode(context, "testrepository").get();
+
+        org.haikuos.pkg.model.Pkg pkg = new Pkg();
+        pkg.setArchitecture(PkgArchitecture.X86);
+        pkg.setDescription("test-description-en");
+        pkg.setSummary("test-summary-en");
+        pkg.setName("testpkg");
+        pkg.setVersion(new PkgVersion("1", minor, "3", "4", 5));
+
+        pkgOrchestrationService.importFrom(
+                context,
+                repository.getObjectId(),
+                pkg);
+
+        context.commitChanges();
+    }
+
+    /**
+ * <p>Given a series of imported packages, the system will try to replicate any localizations sensibly. This test
+     * will check to make sure this works.</p>
+     */
+
+    @Test
+    public void testImportFrom_replicateLocalizationIfEnglishMatches() {
+
+        // create the repository
+
+        integrationTestSupportService.createStandardTestData();
+
+        // import the first version
+
+        importTestPackageWithMinor("2");
+
+        // now configure another language.
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); + org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
+                    context,
+                    pkg,
+                    Collections.singletonList(
+                            Architecture.getByCode(context, "x86").get()
+                    )).get();
+
+            pkgOrchestrationService.updatePkgVersionLocalization(
+                            context,
+                            pkgVersion,
+ NaturalLanguage.getByCode(context, NaturalLanguage.CODE_GERMAN).get(),
+                            "test-summary-de",
+                            "test-description-de");
+
+            context.commitChanges();
+        }
+
+        // now load the next package version in
+
+        importTestPackageWithMinor("3");
+
+        // the second package should have the same german localization
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); + org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
+                    context,
+                    pkg,
+                    Collections.singletonList(
+                            Architecture.getByCode(context, "x86").get()
+                    )).get();
+
+            Assertions.assertThat(pkgVersion.getMajor()).isEqualTo("1");
+            Assertions.assertThat(pkgVersion.getMinor()).isEqualTo("3");
+
+ PkgVersionLocalization localization = pkgVersion.getPkgVersionLocalization(NaturalLanguage.CODE_GERMAN).get();
+
+ Assertions.assertThat(localization.getSummary()).isEqualTo("test-summary-de"); + Assertions.assertThat(localization.getDescription()).isEqualTo("test-description-de");
+        }
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconControllerIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.pkg.controller;
+
+import com.google.common.net.MediaType;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+
+public class PkgIconControllerIT extends AbstractIntegrationTest {
+
+    @Resource
+    PkgIconController pkgIconController;
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    private byte[] getIconData() throws IOException {
+        return getResourceData("/sample-32x32.png");
+    }
+
+    /**
+ * <p>This test works knowing that the test package pkg1 will have a PNG image pre-loaded for it.</p>
+     */
+
+    @Test
+    public void testGet() throws Exception {
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        byte[] imageData = getIconData();
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // ------------------------------------
+        pkgIconController.handleGet(
+                response,
+                32,
+                "png",
+                "pkg1",
+                true);
+        // -----------------------------------
+
+ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); + Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotControllerIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.pkg.controller;
+
+import com.google.common.base.Optional;
+import com.google.common.net.MediaType;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
+import org.haikuos.haikudepotserver.support.ImageHelper;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+
+public class PkgScreenshotControllerIT extends AbstractIntegrationTest {
+
+    @Resource
+    PkgScreenshotController pkgScreenshotController;
+
+    @Resource
+    IntegrationTestSupportService integrationTestSupportService;
+
+    /**
+ * <p>This will return an image that can be used as a sample screenshot.</p>
+     */
+
+    private byte[] getScreenshotData() throws IOException {
+        return getResourceData("/sample-320x240.png");
+    }
+
+    @Test
+    public void testPut() throws Exception {
+
+        setAuthenticatedUserToRoot();
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        byte[] imageData = getScreenshotData();
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        request.setContent(imageData);
+
+        // ------------------------------------
+ pkgScreenshotController.handleAdd(request, response, "png", "pkg1");
+        // -----------------------------------
+
+        // the header should contain the image code
+ String code = response.getHeader(PkgScreenshotController.HEADER_SCREENSHOTCODE);
+        Assertions.assertThat(code).isNotEmpty();
+
+        ObjectContext context = serverRuntime.getContext();
+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, code);
+        Assertions.assertThat(screenshotOptional.isPresent()).isTrue();
+ Assertions.assertThat(screenshotOptional.get().getPkg().getName()).isEqualTo("pkg1"); + Assertions.assertThat(screenshotOptional.get().getPkgScreenshotImage().get().getData()).isEqualTo(imageData);
+
+    }
+
+    @Test
+    public void testGet_noScaling() throws Exception {
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        byte[] imageData = getScreenshotData();
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // ------------------------------------
+        pkgScreenshotController.handleGet(
+                response,
+                640, 480,
+                "png",
+                data.pkg1.getSortedPkgScreenshots().get(0).getCode());
+        // -----------------------------------
+
+ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); + Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
+
+    }
+
+    @Test
+    public void testGet_scaling() throws Exception {
+
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+        byte[] imageData = getScreenshotData();
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // ------------------------------------
+        pkgScreenshotController.handleGet(
+                response,
+                160, 120,
+                "png",
+                data.pkg1.getSortedPkgScreenshots().get(0).getCode());
+        // -----------------------------------
+
+ Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString());
+
+        ImageHelper imageHelper = new ImageHelper();
+ ImageHelper.Size size = imageHelper.derivePngSize(response.getContentAsByteArray());
+        Assertions.assertThat(size.width).isEqualTo(160);
+        Assertions.assertThat(size.height).isEqualTo(120);
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/repository/RepositoryImportServiceIT.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.repository;
+
+import com.google.common.base.Optional;
+import com.google.common.io.Files;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.dataobjects.Architecture;
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.Repository;
+import org.haikuos.haikudepotserver.pkg.model.PkgRepositoryImportJob;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * <p>This test will load in a fake repository HPKR file and will then check to see that it imported correctly.</p>
+ */
+
+public class RepositoryImportServiceIT extends AbstractIntegrationTest {
+
+ public final static long DELAY_PROCESSSUBMITTEDTESTJOB = 60 * 1000; // 60s
+
+    @Resource
+    RepositoryImportService repositoryImportService;
+
+    private void verifyPackage(
+            ObjectContext context,
+            String name) {
+        Optional<Pkg> pkgOptional = Pkg.getByName(context, name);
+        Assertions.assertThat(pkgOptional.isPresent()).isTrue();
+    }
+
+    @Test
+    public void testImportThenCheck() throws Exception {
+
+        File temporaryFile = null;
+
+        try {
+ temporaryFile = File.createTempFile("haikudepotserver-test-",".hpkr");
+
+ // get the test hpkr data and copy it into a temporary file that can be used as a source
+            // for a repository.
+
+ Files.write(getResourceData("/sample-repo.hpkr"), temporaryFile);
+
+ // first setup a fake repository to import that points at the local test HPKR file.
+
+            {
+                ObjectContext context = serverRuntime.getContext();
+ Repository repository = context.newObject(Repository.class);
+                repository.setActive(Boolean.TRUE);
+                repository.setCode("test");
+ repository.setUrl("file://" + temporaryFile.getAbsolutePath()); + repository.setArchitecture(Architecture.getByCode(context, "x86").get());
+                context.commitChanges();
+            }
+
+            // do the import.
+
+ repositoryImportService.submit(new PkgRepositoryImportJob("test"));
+
+            // wait for it to finish.
+
+            {
+                long startMs = System.currentTimeMillis();
+                while(
+                        repositoryImportService.isProcessingSubmittedJobs()
+ && (System.currentTimeMillis() - startMs) < DELAY_PROCESSSUBMITTEDTESTJOB);
+
+                if(repositoryImportService.isProcessingSubmittedJobs()) {
+ throw new IllegalStateException("test processing of the sample repo has taken > "+DELAY_PROCESSSUBMITTEDTESTJOB+"ms");
+                }
+            }
+
+ // now pull out some known packages and make sure they are imported correctly. + // TODO - this is a fairly simplistic test; do some more checks.
+
+            {
+                ObjectContext context = serverRuntime.getContext();
+                verifyPackage(context,"apr");
+                verifyPackage(context,"zlib_x86_gcc2_devel");
+            }
+        }
+        finally {
+            if(null!=temporaryFile) {
+                temporaryFile.delete();
+            }
+        }
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/support/ImageHelperTest.java Wed Apr 9 11:01:59 2014 UTC
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013-2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.support;
+
+import com.google.common.io.ByteStreams;
+import org.junit.Test;
+import static org.fest.assertions.Assertions.assertThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ImageHelperTest {
+
+    private byte[] getData(String leafname) throws IOException {
+        InputStream inputStream = null;
+
+        try {
+            inputStream = this.getClass().getResourceAsStream(leafname);
+            return ByteStreams.toByteArray(inputStream);
+        }
+        finally {
+            Closeables.closeQuietly(inputStream);
+        }
+    }
+
+ private void assertImageSize(String leafname, int width, int height) throws IOException {
+        byte[] png = getData(leafname);
+        ImageHelper imageHelper = new ImageHelper();
+        ImageHelper.Size size = imageHelper.derivePngSize(png);
+        assertThat(size).isNotNull();
+        assertThat(size.width).isEqualTo(width);
+        assertThat(size.height).isEqualTo(height);
+    }
+
+    @Test
+    public void testDerivePngSize() throws IOException {
+        assertImageSize("/sample-260x16.png",260,16);
+        assertImageSize("/sample-32x32.png",32,32);
+        assertImageSize("/sample-16x16.png",16,16);
+    }
+
+    @Test
+ public void testLooksLikeHaikuVectorImageFormat_true() throws IOException {
+         byte[] data = getData("/sample.hvif");
+        ImageHelper imageHelper = new ImageHelper();
+ assertThat(imageHelper.looksLikeHaikuVectorIconFormat(data)).isTrue();
+    }
+
+    @Test
+ public void testLooksLikeHaikuVectorImageFormat_false() throws IOException {
+        byte[] data = getData("/sample-16x16.png");
+        ImageHelper imageHelper = new ImageHelper();
+ assertThat(imageHelper.looksLikeHaikuVectorIconFormat(data)).isFalse();
+    }
+
+}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/AbstractIntegrationTest.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever;
-
-import com.google.common.base.Optional;
-import com.google.common.io.ByteStreams;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.haikuos.haikudepotserver.dataobjects.User;
-import org.haikuos.haikudepotserver.security.AuthenticationHelper;
-import org.haikuos.haikudepotserver.security.AuthenticationService;
-import org.haikuos.haikudepotserver.support.Closeables;
-import org.haikuos.haikudepotserver.support.db.migration.ManagedDatabase;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.Map;
-
-/**
- * <p>This superclass of all of the tests has a hook to run before each integration test. The hook will - * basically delete all of the schema objects and then prompt the database schema migration to again, - * repopulate the database afresh. This ensures that the database is taken from a blank state at the - * start of each test. This is important for tests to maintain their independence. The use a - * 'transaction' over the test is not possible here as the ORM technology isnot bound to a single
- * transaction.</p>
- */
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration({
-        "classpath:/spring/servlet-context.xml",
-        "classpath:/spring/application-context.xml",
-        "classpath:/spring/test.xml"
-})
-public abstract class AbstractIntegrationTest {
-
- protected static Logger logger = LoggerFactory.getLogger(AbstractIntegrationTest.class);
-
- private final static String DATABASEPRODUCTNAME_POSTGRES = "PostgreSQL";
-
-    @Resource
-    ApplicationContext applicationContext;
-
-    @Resource
-    protected ServerRuntime serverRuntime;
-
-    @Resource
-    AuthenticationService authenticationService;
-
-    protected byte[] getResourceData(String path) throws IOException {
-        InputStream inputStream = null;
-
-        try {
-            inputStream = this.getClass().getResourceAsStream(path);
-
-            if(null==inputStream) {
- throw new IllegalStateException("unable to find the test resource; "+path);
-            }
-
-            return ByteStreams.toByteArray(inputStream);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
-    /**
- * <p>Before each test is run, we want to remove all of the database objects and then re-populate
-     * them back again.</p>
-     */
-
-    @Before
-    public void beforeEachTest() {
-
-        logger.info("will prepare for the next test");
-
-        // reset the apache cayenne cache before we go behind its back and
-        // clear out the database for the next test.
-
-        serverRuntime.getDataDomain().getQueryCache().clear();
-        serverRuntime.getDataDomain().getSharedSnapshotCache().clear();
-        logger.info("prep; have cleared out cayenne caches");
-
-        // get all of the databases that are managed.
-
- Map<String,ManagedDatabase> managedDatabases = applicationContext.getBeansOfType(ManagedDatabase.class);
-
-        for(ManagedDatabase managedDatabase : managedDatabases.values()) {
-
-            Connection connection = null;
-            PreparedStatement statement = null;
-
-            try {
- connection = managedDatabase.getDataSource().getConnection();
-                connection.setAutoCommit(false);
-
- String databaseProductName = connection.getMetaData().getDatabaseProductName();
-
- if(!databaseProductName.equals(DATABASEPRODUCTNAME_POSTGRES)) {
-                    throw new IllegalStateException(String.format(
- "the system is designed to be tested against %s database product, but is '%s'",
-                            DATABASEPRODUCTNAME_POSTGRES,
-                            databaseProductName));
-                }
-
-                {
- String statementString = "DROP SCHEMA "+managedDatabase.getSchema()+" CASCADE"; - statement = connection.prepareStatement(statementString);
-                    statement.execute();
-                }
-            }
-            catch(SQLException se) {
- throw new IllegalStateException("a database problem has arisen in preparing for an integration test",se);
-            }
-            finally {
-                Closeables.closeQuietly(statement);
-                Closeables.closeQuietly(connection);
-            }
-
-            managedDatabase.migrate();
- logger.info("prep; did drop database objects for schema '{}' and re-create them", managedDatabase.getSchema());
-        }
-
-
-        logger.info("did prepare for the next test");
-    }
-
-    protected void setAuthenticatedUser(String nickname) {
-        ObjectContext objectContext = serverRuntime.getContext();
- Optional<User> rootUser = User.getByNickname(objectContext, nickname); - AuthenticationHelper.setAuthenticatedUserObjectId(Optional.of(rootUser.get().getObjectId()));
-    }
-
-    /**
- * <p>Some tests will enforce authorization; in order to test the method's actual logic rather than the - * authorization logic, it is sometimes handy to force the authenticated user to be root. This method
-     * will do this.</p>
-     */
-
-    protected void setAuthenticatedUserToRoot() {
-        setAuthenticatedUser("root");
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/IntegrationTestSupportService.java Wed Apr 9 11:01:12 2014 UTC
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever;
-
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.haikuos.haikudepotserver.dataobjects.*;
-import org.haikuos.haikudepotserver.dataobjects.MediaType;
-import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
-import org.haikuos.haikudepotserver.support.Closeables;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.io.InputStream;
-
-/**
- * <p>This class is designed to help out with creating some common test data that can be re-used between tests.</p>
- */
-
-@Service
-public class IntegrationTestSupportService {
-
- protected static Logger logger = LoggerFactory.getLogger(IntegrationTestSupportService.class);
-
-    @Resource
-    ServerRuntime serverRuntime;
-
-    @Resource
-    PkgOrchestrationService pkgService;
-
-    private ObjectContext objectContext = null;
-
-    public ObjectContext getObjectContext() {
-        if(null==objectContext) {
-            objectContext = serverRuntime.getContext();
-        }
-
-        return objectContext;
-    }
-
- private PkgScreenshot addPkgScreenshot(ObjectContext objectContext, Pkg pkg) {
-        InputStream inputStream = null;
-
-        try {
- inputStream = IntegrationTestSupportService.class.getResourceAsStream("/sample-320x240.png"); - return pkgService.storePkgScreenshotImage(inputStream, objectContext, pkg);
-        }
-        catch(Exception e) {
- throw new IllegalStateException("an issue has arisen loading a sample screenshot into a test package",e);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
- private void addPngPkgIcon(ObjectContext objectContext, Pkg pkg, int size) {
-        InputStream inputStream = null;
-
-        try {
- inputStream = this.getClass().getResourceAsStream(String.format("/sample-%dx%d.png",size,size));
-            pkgService.storePkgIconImage(
-                    inputStream,
- MediaType.getByCode(objectContext, com.google.common.net.MediaType.PNG.toString()).get(),
-                    size,
-                    objectContext,
-                    pkg);
-        }
-        catch(Exception e) {
- throw new IllegalStateException("an issue has arisen loading an icon",e);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
-    private void addHvifPkgIcon(ObjectContext objectContext, Pkg pkg) {
-        InputStream inputStream = null;
-
-        try {
- inputStream = this.getClass().getResourceAsStream("/sample.hvif");
-            pkgService.storePkgIconImage(
-                    inputStream,
- MediaType.getByCode(objectContext, MediaType.MEDIATYPE_HAIKUVECTORICONFILE).get(),
-                    null,
-                    objectContext,
-                    pkg);
-        }
-        catch(Exception e) {
- throw new IllegalStateException("an issue has arisen loading an icon",e);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
-    private void addPkgIcons(ObjectContext objectContext, Pkg pkg) {
-        addPngPkgIcon(objectContext, pkg, 16);
-        addPngPkgIcon(objectContext, pkg, 32);
-        addHvifPkgIcon(objectContext, pkg);
-    }
-
-    public StandardTestData createStandardTestData() {
-
-        logger.info("will create standard test data");
-
-        ObjectContext context = getObjectContext();
-        StandardTestData result = new StandardTestData();
-
-        Architecture x86 = Architecture.getByCode(context, "x86").get();
- Architecture x86_gcc2 = Architecture.getByCode(context, "x86_gcc2").get();
-
-        result.repository = context.newObject(Repository.class);
-        result.repository.setActive(Boolean.TRUE);
-        result.repository.setCode("testrepository");
-        result.repository.setArchitecture(x86);
-        result.repository.setUrl("file:///");
-
-        result.pkg1 = context.newObject(Pkg.class);
-        result.pkg1.setActive(true);
-        result.pkg1.setName("pkg1");
-
-        {
- PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); - result.pkg1.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GRAPHICS").get());
-        }
-
-        addPkgScreenshot(context,result.pkg1);
-        addPkgScreenshot(context,result.pkg1);
-        addPkgScreenshot(context,result.pkg1);
-        addPkgIcons(context, result.pkg1);
-
-        result.pkg1Version1x86 = context.newObject(PkgVersion.class);
-        result.pkg1Version1x86.setActive(Boolean.FALSE);
-        result.pkg1Version1x86.setArchitecture(x86);
-        result.pkg1Version1x86.setMajor("1");
-        result.pkg1Version1x86.setMicro("2");
-        result.pkg1Version1x86.setRevision(3);
-        result.pkg1Version1x86.setPkg(result.pkg1);
-        result.pkg1Version1x86.setRepository(result.repository);
-
-        result.pkg1Version2x86 = context.newObject(PkgVersion.class);
-        result.pkg1Version2x86.setActive(Boolean.TRUE);
-        result.pkg1Version2x86.setArchitecture(x86);
-        result.pkg1Version2x86.setMajor("1");
-        result.pkg1Version2x86.setMicro("2");
-        result.pkg1Version2x86.setRevision(4);
-        result.pkg1Version2x86.setPkg(result.pkg1);
-        result.pkg1Version2x86.setRepository(result.repository);
-
-        {
- PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); - pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); - pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); - result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
-        }
-
-        {
- PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_SPANISH).get()); - pkgVersionLocalization.setDescription("pkg1Version2DescriptionSpanish"); - pkgVersionLocalization.setSummary("pkg1Version2SummarySpanish"); - result.pkg1Version2x86.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
-        }
-
-        result.pkg1Version2x86_gcc2 = context.newObject(PkgVersion.class);
-        result.pkg1Version2x86_gcc2.setActive(Boolean.TRUE);
-        result.pkg1Version2x86_gcc2.setArchitecture(x86_gcc2);
-        result.pkg1Version2x86_gcc2.setMajor("1");
-        result.pkg1Version2x86_gcc2.setMicro("2");
-        result.pkg1Version2x86_gcc2.setRevision(4);
-        result.pkg1Version2x86_gcc2.setPkg(result.pkg1);
-        result.pkg1Version2x86_gcc2.setRepository(result.repository);
-
- // this is the same as the x86 version so that comparisons with English will happen.
-
-        {
- PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); - pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get()); - pkgVersionLocalization.setDescription("pkg1Version2DescriptionEnglish"); - pkgVersionLocalization.setSummary("pkg1Version2SummaryEnglish"); - result.pkg1Version2x86_gcc2.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
-        }
-
-        result.pkg2 = context.newObject(Pkg.class);
-        result.pkg2.setActive(true);
-        result.pkg2.setName("pkg2");
-
-        result.pkg2Version1 = context.newObject(PkgVersion.class);
-        result.pkg2Version1.setActive(Boolean.TRUE);
-        result.pkg2Version1.setArchitecture(x86);
-        result.pkg2Version1.setMajor("1");
-        result.pkg2Version1.setMicro("2");
-        result.pkg2Version1.setRevision(3);
-        result.pkg2Version1.setPkg(result.pkg2);
-        result.pkg2Version1.setRepository(result.repository);
-
-        result.pkg3 = context.newObject(Pkg.class);
-        result.pkg3.setActive(true);
-        result.pkg3.setName("pkg3");
-
-        result.pkg3Version1 = context.newObject(PkgVersion.class);
-        result.pkg3Version1.setActive(Boolean.TRUE);
-        result.pkg3Version1.setArchitecture(x86);
-        result.pkg3Version1.setMajor("1");
-        result.pkg3Version1.setMicro("2");
-        result.pkg3Version1.setRevision(3);
-        result.pkg3Version1.setPkg(result.pkg3);
-        result.pkg3Version1.setRepository(result.repository);
-
-        context.commitChanges();
-
-        logger.info("did create standard test data");
-
-        return result;
-    }
-
-    /**
- * <p>This class is a container that carries some basic test-case data.</p>
-     */
-
-    public static class StandardTestData {
-
-        public Repository repository;
-
-        public Pkg pkg1;
-        public PkgVersion pkg1Version1x86;
-        public PkgVersion pkg1Version2x86;
-        public PkgVersion pkg1Version2x86_gcc2;
-
-        public Pkg pkg2;
-        public PkgVersion pkg2Version1;
-
-        public Pkg pkg3;
-        public PkgVersion pkg3Version1;
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/CaptchaApiIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1;
-
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.api1.CaptchaApi;
-import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaRequest; -import org.haikuos.haikudepotserver.api1.model.captcha.GenerateCaptchaResult;
-import org.haikuos.haikudepotserver.captcha.model.CaptchaRepository;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-
-public class CaptchaApiIT extends AbstractIntegrationTest {
-
-    @Resource
-    CaptchaApi captchaApi;
-
-    @Resource
-    CaptchaRepository captchaRepository;
-
-    /**
- * <p>This is a bit of a limited test because there is nothing to actually check without having a person - * to interpret the image. In any case, it will provide for an element of just exercising the API to make
-     * sure that it at least does not fail.</p>
-     */
-
-    @Test
-    public void testGenerateCaptcha() {
-
-        // ------------------------------------
-
- GenerateCaptchaResult result = captchaApi.generateCaptcha(new GenerateCaptchaRequest());
-
-        // ------------------------------------
-
-        Assertions.assertThat(result.token).isNotEmpty();
- Assertions.assertThat(captchaRepository.get(result.token)).isNotNull();
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/MiscelaneousApiIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.api1.MiscellaneousApi;
-import org.haikuos.haikudepotserver.api1.model.AuthorizationTargetType;
-import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
-import org.haikuos.haikudepotserver.dataobjects.PkgCategory;
-import org.haikuos.haikudepotserver.security.model.Permission;
-import org.haikuos.haikudepotserver.api1.model.miscellaneous.*;
-import org.haikuos.haikudepotserver.support.RuntimeInformationService;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.util.List;
-
-public class MiscelaneousApiIT extends AbstractIntegrationTest {
-
-    @Resource
-    MiscellaneousApi miscellaneousApi;
-
-    @Resource
-    RuntimeInformationService runtimeInformationService;
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    private void assertTargetAndPermission(
-            IntegrationTestSupportService.StandardTestData data,
- CheckAuthorizationResult.AuthorizationTargetAndPermission targetAndPermission,
-            boolean result) {
- Assertions.assertThat(targetAndPermission.permissionCode).isEqualTo(Permission.PKG_EDITICON.name()); - Assertions.assertThat(targetAndPermission.targetIdentifier).isEqualTo(data.pkg1.getName()); - Assertions.assertThat(targetAndPermission.targetType).isEqualTo(AuthorizationTargetType.PKG); - Assertions.assertThat(targetAndPermission.authorized).isEqualTo(result);
-    }
-
-    @Test
-    public void testGetAllPkgCategories() {
-
-        // ------------------------------------
- GetAllPkgCategoriesResult result = miscellaneousApi.getAllPkgCategories(new GetAllPkgCategoriesRequest());
-        // ------------------------------------
-
-        ObjectContext objectContext = serverRuntime.getContext();
-
- List<PkgCategory> pkgCategories = PkgCategory.getAll(objectContext);
-
- Assertions.assertThat(pkgCategories.size()).isEqualTo(result.pkgCategories.size());
-
-        for(int i=0;i<pkgCategories.size();i++) {
-            PkgCategory pkgCategory = pkgCategories.get(i);
- GetAllPkgCategoriesResult.PkgCategory apiPkgCategory = result.pkgCategories.get(i); - Assertions.assertThat(pkgCategory.getName()).isEqualTo(apiPkgCategory.name); - Assertions.assertThat(pkgCategory.getCode()).isEqualTo(apiPkgCategory.code);
-        }
-    }
-
-    @Test
-    public void testGetAllNaturalLanguages() {
-
-        // ------------------------------------
- GetAllNaturalLanguagesResult result = miscellaneousApi.getAllNaturalLanguages(new GetAllNaturalLanguagesRequest());
-        // ------------------------------------
-
-        ObjectContext objectContext = serverRuntime.getContext();
-
- List<NaturalLanguage> naturalLanguages = NaturalLanguage.getAll(objectContext);
-
- Assertions.assertThat(naturalLanguages.size()).isEqualTo(result.naturalLanguages.size());
-
-        for(int i=0;i<naturalLanguages.size();i++) {
-            NaturalLanguage naturalLanguage = naturalLanguages.get(i);
- GetAllNaturalLanguagesResult.NaturalLanguage apiNaturalLanguage = result.naturalLanguages.get(i); - Assertions.assertThat(naturalLanguage.getName()).isEqualTo(apiNaturalLanguage.name); - Assertions.assertThat(naturalLanguage.getCode()).isEqualTo(apiNaturalLanguage.code);
-        }
-    }
-
-    @Test
-    public void checkAuthorizationRequest_asUnauthenticated() {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
- CheckAuthorizationRequest request = new CheckAuthorizationRequest();
-        request.targetAndPermissions = Lists.newArrayList();
-
- request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
-                AuthorizationTargetType.PKG,
-                data.pkg1.getName(),
-                Permission.PKG_EDITICON.name()));
-
-        // ------------------------------------
- CheckAuthorizationResult result = miscellaneousApi.checkAuthorization(request);
-        // ------------------------------------
-
- Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); - assertTargetAndPermission(data, result.targetAndPermissions.get(0), false);
-
-    }
-
- // TODO : when some more sophisticated cases are available; implement some better tests
-    @Test
-    public void checkAuthorizationRequest_asRoot() {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
-        setAuthenticatedUserToRoot();
-
- CheckAuthorizationRequest request = new CheckAuthorizationRequest();
-        request.targetAndPermissions = Lists.newArrayList();
-
- request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
-                AuthorizationTargetType.PKG,
-                data.pkg1.getName(),
-                Permission.PKG_EDITICON.name()));
-
-        // ------------------------------------
- CheckAuthorizationResult result = miscellaneousApi.checkAuthorization(request);
-        // ------------------------------------
-
- Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); - assertTargetAndPermission(data, result.targetAndPermissions.get(0), true);
-
-    }
-
-    @Test
-    public void getRuntimeInformation_asUnauthenticated() {
-
-        // ------------------------------------
- GetRuntimeInformationResult result = miscellaneousApi.getRuntimeInformation(new GetRuntimeInformationRequest());
-        // ------------------------------------
-
-        Assertions.assertThat(result.javaVersion).isNull();
-    }
-
-    @Test
-    public void getRuntimeInformation_asRoot() {
-
-        setAuthenticatedUserToRoot();
-
-        // ------------------------------------
- GetRuntimeInformationResult result = miscellaneousApi.getRuntimeInformation(new GetRuntimeInformationRequest());
-        // ------------------------------------
-
- Assertions.assertThat(result.javaVersion).isEqualTo(runtimeInformationService.getJavaVersion());
-
-    }
-
-    @Test
-    public void testGetAllMessages() throws Exception {
-
-        // ------------------------------------
- GetAllMessagesResult result = miscellaneousApi.getAllMessages(new GetAllMessagesRequest(NaturalLanguage.CODE_ENGLISH));
-        // ------------------------------------
-
- Assertions.assertThat(result.messages.get("test.it")).isEqualTo("Test line for integration testing");
-
-    }
-
-    private Optional<GetAllArchitecturesResult.Architecture> isPresent(
-            GetAllArchitecturesResult result,
-            final String architectureCode) {
- return Iterables.tryFind(result.architectures, new Predicate<GetAllArchitecturesResult.Architecture>() {
-            @Override
- public boolean apply(GetAllArchitecturesResult.Architecture architecture) {
-                return architecture.code.equals(architectureCode);
-            }
-        });
-    }
-
-    @Test
-    public void testGetAllArchitectures() {
-
-        // ------------------------------------
- GetAllArchitecturesResult result = miscellaneousApi.getAllArchitectures(new GetAllArchitecturesRequest());
-        // ------------------------------------
-
-        // not sure what architectures there may be in the future, but
-        // we will just check for a couple that we know to be there.
-
- Assertions.assertThat(isPresent(result,"x86").isPresent()).isTrue(); - Assertions.assertThat(isPresent(result,"x86_gcc2").isPresent()).isTrue(); - Assertions.assertThat(isPresent(result,"mips").isPresent()).isFalse();
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Wed Apr 9 11:01:12 2014 UTC
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.net.MediaType;
-import com.googlecode.jsonrpc4j.Base64;
-import junit.framework.Assert;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.api1.PkgApi;
-import org.haikuos.haikudepotserver.api1.model.pkg.*;
-import org.haikuos.haikudepotserver.api1.support.BadPkgIconException;
-import org.haikuos.haikudepotserver.api1.support.LimitExceededException;
-import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
-import org.haikuos.haikudepotserver.dataobjects.*;
-import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
-import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.util.Collections;
-import java.util.List;
-
-public class PkgApiIT extends AbstractIntegrationTest {
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    @Resource
-    PkgApi pkgApi;
-
-    @Resource
-    PkgOrchestrationService pkgService;
-
-    @Test
-    public void testUpdatePkgCategories() throws Exception {
-
-        setAuthenticatedUserToRoot();
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
-        // setup some categories as a start condition.
-
-        {
-            ObjectContext context = serverRuntime.getContext();
-            Pkg pkg = Pkg.getByName(context, data.pkg1.getName()).get();
-
-            {
- PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GAMES").get()); - pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
-            }
-
-            {
- PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "BUSINESS").get()); - pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
-            }
-
-            context.commitChanges();
-        }
-
- UpdatePkgCategoriesRequest request = new UpdatePkgCategoriesRequest();
-        request.pkgName = data.pkg1.getName();
- request.pkgCategoryCodes = ImmutableList.of("BUSINESS", "DEVELOPMENT");
-
-        // ------------------------------------
-        pkgApi.updatePkgCategories(request);
-        // ------------------------------------
-
- // now we need to check on those categories. GAMES should have gone, BUSINESS should remain
-        // and DEVELOPMENT should be added.
-
-        {
-            ObjectContext context = serverRuntime.getContext();
-            Pkg pkg = Pkg.getByName(context, data.pkg1.getName()).get();
-
- Assertions.assertThat(ImmutableSet.of("BUSINESS", "DEVELOPMENT")).isEqualTo(
-                ImmutableSet.copyOf(Iterables.transform(
-                        pkg.getPkgPkgCategories(),
-                        new Function<PkgPkgCategory, String>() {
-                            @Override
-                            public String apply(PkgPkgCategory input) {
-                                return input.getPkgCategory().getCode();
-                            }
-                        }
-                ))
-            );
-        }
-
-    }
-
-    @Test
-    public void searchPkgsTest() {
-        integrationTestSupportService.createStandardTestData();
-
-        SearchPkgsRequest request = new SearchPkgsRequest();
-        request.architectureCode = "x86";
-        request.expression = "pk";
-        request.expressionType = SearchPkgsRequest.ExpressionType.CONTAINS;
-        request.limit = 2;
-        request.offset = 0;
-
-        // ------------------------------------
-        SearchPkgsResult result = pkgApi.searchPkgs(request);
-        // ------------------------------------
-
-        Assertions.assertThat(result.hasMore).isTrue();
-        Assertions.assertThat(result.items.size()).isEqualTo(2);
-        Assertions.assertThat(result.items.get(0).name).isEqualTo("pkg1");
-        Assertions.assertThat(result.items.get(1).name).isEqualTo("pkg2");
-    }
-
-    /**
- * <p>In this test, an German localization is requested, but there is no localization present for German so it will
-     * fall back English.</p>
-     */
-
-    @Test
-    public void testGetPkg_found() throws ObjectNotFoundException {
-        integrationTestSupportService.createStandardTestData();
-
-        GetPkgRequest request = new GetPkgRequest();
-        request.architectureCode = "x86";
-        request.name = "pkg1";
-        request.versionType = PkgVersionType.LATEST;
-        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
-
-        // ------------------------------------
-        GetPkgResult result = pkgApi.getPkg(request);
-        // ------------------------------------
-
-        Assertions.assertThat(result.name).isEqualTo("pkg1");
-        Assertions.assertThat(result.versions.size()).isEqualTo(1);
- Assertions.assertThat(result.versions.get(0).architectureCode).isEqualTo("x86");
-        Assertions.assertThat(result.versions.get(0).major).isEqualTo("1");
-        Assertions.assertThat(result.versions.get(0).micro).isEqualTo("2");
- Assertions.assertThat(result.versions.get(0).revision).isEqualTo(4); - Assertions.assertThat(result.versions.get(0).naturalLanguageCode).isEqualTo(NaturalLanguage.CODE_ENGLISH); - Assertions.assertThat(result.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); - Assertions.assertThat(result.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
-    }
-
-    @Test
-    public void testGetPkg_notFound() {
-        integrationTestSupportService.createStandardTestData();
-
-        GetPkgRequest request = new GetPkgRequest();
-        request.architectureCode = "x86";
-        request.name = "pkg9";
-        request.versionType = PkgVersionType.LATEST;
-        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
-
-        try {
-
-            // ------------------------------------
-            pkgApi.getPkg(request);
-            // ------------------------------------
-
- Assert.fail("expected an instance of "+ObjectNotFoundException.class.getSimpleName()+" to be thrown, but was not");
-        }
-        catch(ObjectNotFoundException onfe) {
- Assertions.assertThat(onfe.getEntityName()).isEqualTo(Pkg.class.getSimpleName());
-            Assertions.assertThat(onfe.getIdentifier()).isEqualTo("pkg9");
-        }
-        catch(Throwable th) {
- Assert.fail("expected an instance of "+ObjectNotFoundException.class.getSimpleName()+" to be thrown, but "+th.getClass().getSimpleName()+" was instead");
-        }
-    }
-
-    @Test
-    public void testGetPkgIcons() throws Exception {
-
-        integrationTestSupportService.createStandardTestData();
-
-        // ------------------------------------
- GetPkgIconsResult result = pkgApi.getPkgIcons(new GetPkgIconsRequest("pkg1"));
-        // ------------------------------------
-
-        Assertions.assertThat(result.pkgIcons.size()).isEqualTo(3);
-        // check more stuff...
-
-    }
-
-    /**
-     * <p>Here we are trying to load the HVIF data in as PNG images.</p>
-     */
-
-    @Test
-    public void testConfigurePkgIcon_badData() throws Exception {
-
-        setAuthenticatedUserToRoot();
-        integrationTestSupportService.createStandardTestData();
-
-        byte[] sampleHvif = getResourceData("/sample.hvif");
-
-        ConfigurePkgIconRequest request = new ConfigurePkgIconRequest();
-
-        request.pkgName = "pkg1";
-        request.pkgIcons = ImmutableList.of(
-                new ConfigurePkgIconRequest.PkgIcon(
-                        MediaType.PNG.toString(),
-                        16,
-                        Base64.encodeBytes(sampleHvif)),
-                new ConfigurePkgIconRequest.PkgIcon(
-                        MediaType.PNG.toString(),
-                        32,
-                        Base64.encodeBytes(sampleHvif)),
-                new ConfigurePkgIconRequest.PkgIcon(
- org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE,
-                        null,
-                        Base64.encodeBytes(sampleHvif)));
-
-        try {
-
-            // ------------------------------------
-            pkgApi.configurePkgIcon(request);
-            // ------------------------------------
-
- Assert.fail("expected an instance of '"+BadPkgIconException.class.getSimpleName()+"' to have been thrown");
-
-        }
-        catch(BadPkgIconException bpie) {
-
- // This is the first one that failed so we should get this come up as the exception that was thrown.
-
-            Assertions.assertThat(bpie.getSize()).isEqualTo(16);
- Assertions.assertThat(bpie.getMediaTypeCode()).isEqualTo(MediaType.PNG.toString());
-        }
-    }
-
-
-    /**
-     * <p>This test will configure the icons for the package.</p>
-     */
-
-    @Test
-    public void testConfigurePkgIcon_ok() throws Exception {
-
-        setAuthenticatedUserToRoot();
-        integrationTestSupportService.createStandardTestData();
-
-        byte[] sample16 = getResourceData("/sample-16x16.png");
-        byte[] sample32 = getResourceData("/sample-32x32.png");
-        byte[] sampleHvif = getResourceData("/sample.hvif");
-
-        ConfigurePkgIconRequest request = new ConfigurePkgIconRequest();
-
-        request.pkgName = "pkg1";
-        request.pkgIcons = ImmutableList.of(
-                new ConfigurePkgIconRequest.PkgIcon(
-                        MediaType.PNG.toString(),
-                        16,
-                        Base64.encodeBytes(sample16)),
-                new ConfigurePkgIconRequest.PkgIcon(
-                        MediaType.PNG.toString(),
-                        32,
-                        Base64.encodeBytes(sample32)),
-                new ConfigurePkgIconRequest.PkgIcon(
- org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE,
-                        null,
-                        Base64.encodeBytes(sampleHvif)));
-
-        // ------------------------------------
-        pkgApi.configurePkgIcon(request);
-        // ------------------------------------
-
-        {
-            ObjectContext objectContext = serverRuntime.getContext();
- Optional<Pkg> pkgOptionalafter = Pkg.getByName(objectContext, "pkg1");
-
-            org.haikuos.haikudepotserver.dataobjects.MediaType mediaTypePng
- = org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(
-                    objectContext,
-                    MediaType.PNG.toString()).get();
-
- org.haikuos.haikudepotserver.dataobjects.MediaType mediaTypeHvif - = org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(
-                    objectContext,
- org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE).get();
-
- Assertions.assertThat(pkgOptionalafter.get().getPkgIcons().size()).isEqualTo(3);
-
- Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon16Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 16); - Assertions.assertThat(pkgIcon16Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample16);
-
- Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIcon32Optional = pkgOptionalafter.get().getPkgIcon(mediaTypePng, 32); - Assertions.assertThat(pkgIcon32Optional.get().getPkgIconImage().get().getData()).isEqualTo(sample32);
-
- Optional<org.haikuos.haikudepotserver.dataobjects.PkgIcon> pkgIconHvifOptional = pkgOptionalafter.get().getPkgIcon(mediaTypeHvif, null); - Assertions.assertThat(pkgIconHvifOptional.get().getPkgIconImage().get().getData()).isEqualTo(sampleHvif);
-        }
-    }
-
-    /**
- * <p>This test knows that an icon exists for pkg1 and then removes it.</p>
-     */
-
-    @Test
-    public void testRemoveIcon() throws Exception {
-
-        setAuthenticatedUserToRoot();
-
-        integrationTestSupportService.createStandardTestData();
-
-        {
-            ObjectContext objectContext = serverRuntime.getContext();
- Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); - Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(3); // 16 and 32 px sizes + hvif
-        }
-
-        // ------------------------------------
-        pkgApi.removePkgIcon(new RemovePkgIconRequest("pkg1"));
-        // ------------------------------------
-
-        {
-            ObjectContext objectContext = serverRuntime.getContext();
- Optional<Pkg> pkgOptionalBefore = Pkg.getByName(objectContext, "pkg1"); - Assertions.assertThat(pkgOptionalBefore.get().getPkgIcons().size()).isEqualTo(0);
-        }
-    }
-
-    /**
- * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
-     */
-
-    @Test
-    public void testGetPkgScreenshots() throws ObjectNotFoundException {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
-        // ------------------------------------
- GetPkgScreenshotsResult result = pkgApi.getPkgScreenshots(new GetPkgScreenshotsRequest(data.pkg1.getName()));
-        // ------------------------------------
-
- Assertions.assertThat(result.items.size()).isEqualTo(data.pkg1.getPkgScreenshots().size()); - List<PkgScreenshot> sortedScreenshots = data.pkg1.getSortedPkgScreenshots();
-
-        for(int i=0;i<sortedScreenshots.size();i++) {
-            PkgScreenshot pkgScreenshot = sortedScreenshots.get(i);
- org.haikuos.haikudepotserver.api1.model.pkg.PkgScreenshot apiPkgScreenshot = result.items.get(i); - Assertions.assertThat(pkgScreenshot.getCode()).isEqualTo(apiPkgScreenshot.code);
-            Assertions.assertThat(pkgScreenshot.getWidth()).isEqualTo(320);
- Assertions.assertThat(pkgScreenshot.getHeight()).isEqualTo(240); - Assertions.assertThat(pkgScreenshot.getLength()).isEqualTo(41296);
-        }
-    }
-
-    /**
- * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
-     */
-
-    @Test
-    public void testGetPkgScreenshot() throws ObjectNotFoundException {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        String code = data.pkg1.getSortedPkgScreenshots().get(0).getCode();
-
-        // ------------------------------------
- GetPkgScreenshotResult result = pkgApi.getPkgScreenshot(new GetPkgScreenshotRequest(code));
-        // ------------------------------------
-
-        Assertions.assertThat(result.code).isEqualTo(code);
-        Assertions.assertThat(result.width).isEqualTo(320);
-        Assertions.assertThat(result.height).isEqualTo(240);
-        Assertions.assertThat(result.length).isEqualTo(41296);
-    }
-
-    /**
- * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
-     */
-
-    @Test
-    public void testRemovePkgScreenshot() throws Exception {
-        setAuthenticatedUserToRoot();
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); - List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
-
-        if(sortedScreenshotsBefore.size() < 2) {
- throw new IllegalStateException("the test cannot run without more than two screenshots");
-        }
-
-        final String code1 = sortedScreenshotsBefore.get(1).getCode();
-
-        // ------------------------------------
-        pkgApi.removePkgScreenshot(new RemovePkgScreenshotRequest(code1));
-        // ------------------------------------
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();
-
- Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(sortedScreenshotsBefore.size()-1);
-
- Assertions.assertThat(Iterables.tryFind(sortedScreenshotsAfter, new Predicate<PkgScreenshot>() {
-            @Override
-            public boolean apply(PkgScreenshot pkgScreenshot) {
-                return pkgScreenshot.getCode().equals(code1);
-            }
-        }).isPresent()).isFalse();
-    }
-
-    /**
- * <p>This test assumes that the test data has a pkg1 with three screenshots associated with it.</p>
-     */
-
-    @Test
-    public void testReorderPkgScreenshots() throws Exception {
-        setAuthenticatedUserToRoot();
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData(); - List<PkgScreenshot> sortedScreenshotsBefore = data.pkg1.getSortedPkgScreenshots();
-
-        if(3 != sortedScreenshotsBefore.size()) {
- throw new IllegalStateException("the test requires that pkg1 has three screenshots associated with it");
-        }
-
-        // ------------------------------------
-        pkgApi.reorderPkgScreenshots(new ReorderPkgScreenshotsRequest(
-                data.pkg1.getName(),
-                ImmutableList.of(
-                        sortedScreenshotsBefore.get(2).getCode(),
-                        sortedScreenshotsBefore.get(0).getCode()
-                )
-        ));
-        // ------------------------------------
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - List<PkgScreenshot> sortedScreenshotsAfter = pkgOptional.get().getSortedPkgScreenshots();
-
-        Assertions.assertThat(sortedScreenshotsAfter.size()).isEqualTo(3);
- Assertions.assertThat(sortedScreenshotsAfter.get(0).getCode()).isEqualTo(sortedScreenshotsBefore.get(2).getCode()); - Assertions.assertThat(sortedScreenshotsAfter.get(1).getCode()).isEqualTo(sortedScreenshotsBefore.get(0).getCode()); - Assertions.assertThat(sortedScreenshotsAfter.get(2).getCode()).isEqualTo(sortedScreenshotsBefore.get(1).getCode());
-    }
-
- private void testUpdatePkgVersionLocalization(String naturalLanguageCode) throws Exception {
-        setAuthenticatedUserToRoot();
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
- UpdatePkgVersionLocalizationRequest request = new UpdatePkgVersionLocalizationRequest();
-        request.pkgName = data.pkg1.getName();
-        request.architectureCode = "x86";
-        request.description = "testDescription";
-        request.naturalLanguageCode = naturalLanguageCode;
-        request.summary = "testSummary";
- request.replicateToOtherArchitecturesWithSameEnglishContent = Boolean.TRUE;
-
-        // ------------------------------------
-        pkgApi.updatePkgVersionLocalization(request);
-        // ------------------------------------
-
-        {
-            ObjectContext context = serverRuntime.getContext();
- Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
-                    context,
-                    pkgOptional.get(),
- Collections.singletonList(Architecture.getByCode(context, "x86").get()));
-
- Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
-
- Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); - Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
-        }
-
- // check that the data is copied to other architecture. A x86_gcc2 package version is known to be
-        // present in the test data.
-
-        {
-            ObjectContext context = serverRuntime.getContext();
- Optional<Pkg> pkgOptional = Pkg.getByName(context, data.pkg1.getName()); - Optional<PkgVersion> pkgVersionOptional = PkgVersion.getLatestForPkg(
-                    context,
-                    pkgOptional.get(),
- Collections.singletonList(Architecture.getByCode(context, "x86_gcc2").get()));
-
- Optional<PkgVersionLocalization> pkgVersionLocalizationOptional = pkgVersionOptional.get().getPkgVersionLocalization(naturalLanguageCode);
-
- Assertions.assertThat(pkgVersionLocalizationOptional.get().getSummary()).isEqualTo("testSummary"); - Assertions.assertThat(pkgVersionLocalizationOptional.get().getDescription()).isEqualTo("testDescription");
-        }
-
-    }
-
-    @Test
- public void testUpdatePkgVersionLocalization_existingNaturalLanguage() throws Exception {
-        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_SPANISH);
-    }
-
-    @Test
- public void testUpdatePkgVersionLocalization_newNaturalLanguage() throws Exception {
-        testUpdatePkgVersionLocalization(NaturalLanguage.CODE_GERMAN);
-    }
-
-    /**
- * <p>This test requests german and english, but only english ispresent so needs to check that the output
-     * contains only the english data.</p>
-     */
-
-    @Test
-    public void testGetPkgVersionLocalizations() throws Exception {
-        setAuthenticatedUserToRoot();
-
-        integrationTestSupportService.createStandardTestData();
-
- GetPkgVersionLocalizationsRequest request = new GetPkgVersionLocalizationsRequest();
-        request.architectureCode = "x86";
- request.naturalLanguageCodes = ImmutableList.of(NaturalLanguage.CODE_ENGLISH, NaturalLanguage.CODE_GERMAN);
-        request.pkgName = "pkg1";
-
-        // ------------------------------------
- GetPkgVersionLocalizationsResult result = pkgApi.getPkgVersionLocalizations(request);
-        // ------------------------------------
-
- Assertions.assertThat(result.pkgVersionLocalizations.size()).isEqualTo(1); - Assertions.assertThat(result.pkgVersionLocalizations.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); - Assertions.assertThat(result.pkgVersionLocalizations.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish");
-    }
-
-    /**
- * <p>This test is just checking that if too many packages are requested that it throws the right
-     * sort of exception.</p>
-     */
-
-    @Test
-    public void testGetBulkPkg__limitExceeded() throws Exception {
-
-        GetBulkPkgRequest request = new GetBulkPkgRequest();
- request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
-        request.versionType = PkgVersionType.LATEST;
-        request.architectureCode = "x86";
-        request.naturalLanguageCode = "en";
-        request.pkgNames = Lists.newArrayList();
-
-        while(request.pkgNames.size() < PkgApi.GETBULKPKG_LIMIT + 1) {
-            request.pkgNames.add("pkg");
-        }
-
-        try {
-            // ------------------------------------
-            pkgApi.getBulkPkg(request);
-            // ------------------------------------
- Assert.fail("expected an instance of "+ LimitExceededException.class.getSimpleName()+" to be thrown");
-        }
-        catch(LimitExceededException lee) {
-            // expected
-        }
-    }
-
-    @Test
-    public void testGetBulkPkg__ok() throws Exception {
-        integrationTestSupportService.createStandardTestData();
-
-        GetBulkPkgRequest request = new GetBulkPkgRequest();
- request.filter = ImmutableList.copyOf(GetBulkPkgRequest.Filter.values());
-        request.versionType = PkgVersionType.LATEST;
-        request.architectureCode = "x86";
-        request.naturalLanguageCode = "en";
-        request.pkgNames = ImmutableList.of("pkg1","pkg2","pkg3");
-
-        // ------------------------------------
-        GetBulkPkgResult result = pkgApi.getBulkPkg(request);
-        // ------------------------------------
-
-        Assertions.assertThat(result.pkgs.size()).isEqualTo(3);
-
-        // now check pkg1 because it has some in-depth data on it.
-
- GetBulkPkgResult.Pkg pkg1 = Iterables.tryFind(result.pkgs, new Predicate<GetBulkPkgResult.Pkg>() {
-            @Override
-            public boolean apply(GetBulkPkgResult.Pkg input) {
-                return input.name.equals("pkg1");
-            }
-        }).get();
-
-        Assertions.assertThat(pkg1.name).isEqualTo("pkg1");
-        Assertions.assertThat(pkg1.modifyTimestamp).isNotNull();
-
-        Assertions.assertThat(pkg1.pkgCategoryCodes.size()).isEqualTo(1);
- Assertions.assertThat(pkg1.pkgCategoryCodes.get(0)).isEqualTo("GRAPHICS");
-
-        Assertions.assertThat(pkg1.userRatingAverage).isNotNull();
- Assertions.assertThat(pkg1.userRatingAverage).isGreaterThanOrEqualTo(0.0f); - Assertions.assertThat(pkg1.userRatingAverage).isLessThanOrEqualTo(5.0f);
-
- // there are three screen-shots loaded, but they are all the same so we can just check that the first
-        // one is correct.
-        Assertions.assertThat(pkg1.pkgScreenshots.size()).isEqualTo(3);
-        Assertions.assertThat(pkg1.pkgScreenshots.get(0).code).isNotNull();
- Assertions.assertThat(pkg1.pkgScreenshots.get(0).width).isEqualTo(320); - Assertions.assertThat(pkg1.pkgScreenshots.get(0).height).isEqualTo(240);
-
- // basic check here to make sure that the HPKR data is able to be flagged as being there.
-        Assertions.assertThat(pkg1.pkgIcons.size()).isEqualTo(3);
-        Assertions.assertThat(
- Iterables.tryFind(pkg1.pkgIcons, new Predicate<org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
-            @Override
- public boolean apply(org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon input) { - return input.mediaTypeCode.equals(org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE);
-            }
-        }).isPresent()).isTrue();
-
-        Assertions.assertThat(pkg1.versions.size()).isEqualTo(1);
- Assertions.assertThat(pkg1.versions.get(0).naturalLanguageCode).isEqualTo("en"); - Assertions.assertThat(pkg1.versions.get(0).description).isEqualTo("pkg1Version2DescriptionEnglish"); - Assertions.assertThat(pkg1.versions.get(0).summary).isEqualTo("pkg1Version2SummaryEnglish"); - Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isNotNull(); - Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isGreaterThanOrEqualTo(0.0f); - Assertions.assertThat(pkg1.versions.get(0).userRatingAverage).isLessThanOrEqualTo(5.0f);
-        Assertions.assertThat(pkg1.versions.get(0).major).isEqualTo("1");
-        Assertions.assertThat(pkg1.versions.get(0).micro).isEqualTo("2");
-        Assertions.assertThat(pkg1.versions.get(0).revision).isEqualTo(4);
-        Assertions.assertThat(pkg1.versions.get(0).preRelease).isNull();
-        Assertions.assertThat(pkg1.versions.get(0).minor).isNull();
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/RepositoryApiIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1;
-
-import com.google.common.base.Optional;
-import junit.framework.Assert;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.api1.RepositoryApi;
-import org.haikuos.haikudepotserver.api1.model.pkg.SearchPkgsRequest;
-import org.haikuos.haikudepotserver.api1.model.repository.*;
-import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
-import org.haikuos.haikudepotserver.api1.support.ValidationException;
-import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.util.Collections;
-
-public class RepositoryApiIT extends AbstractIntegrationTest {
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    @Resource
-    RepositoryApi repositoryApi;
-
-    @Test
-    public void testUpdateRepository() throws ObjectNotFoundException {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        setAuthenticatedUserToRoot();
-
-        UpdateRepositoryRequest request = new UpdateRepositoryRequest();
-        request.active = false;
-        request.code = data.repository.getCode();
- request.filter = Collections.singletonList(UpdateRepositoryRequest.Filter.ACTIVE);
-
-        // ------------------------------------
-        repositoryApi.updateRepository(request);
-        // ------------------------------------
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<Repository> repositoryOptional = Repository.getByCode(context, data.repository.getCode());
-        Assertions.assertThat(repositoryOptional.isPresent()).isTrue();
- Assertions.assertThat(repositoryOptional.get().getActive()).isFalse();
-
-    }
-
-    @Test
-    public void searchPkgsTest() {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
- SearchRepositoriesRequest request = new SearchRepositoriesRequest();
-        request.expression = "test";
-        request.expressionType = SearchPkgsRequest.ExpressionType.CONTAINS;
-        request.limit = 2;
-        request.offset = 0;
-
-        // ------------------------------------
- SearchRepositoriesResult result = repositoryApi.searchRepositories(request);
-        // ------------------------------------
-
-        Assertions.assertThat(result.hasMore).isFalse();
-        Assertions.assertThat(result.items.size()).isEqualTo(1);
- Assertions.assertThat(result.items.get(0).code).isEqualTo("testrepository");
-    }
-
-    @Test
-    public void getRepositoryTest() throws Exception {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-
-        GetRepositoryRequest request = new GetRepositoryRequest();
-        request.code = "testrepository";
-
-        // ------------------------------------
-        GetRepositoryResult result = repositoryApi.getRepository(request);
-        // ------------------------------------
-
-        Assertions.assertThat(result.active).isTrue();
-        Assertions.assertThat(result.architectureCode).isEqualTo("x86");
-        Assertions.assertThat(result.url).isEqualTo("file:///");
-    }
-
-    @Test
-    public void testCreateRepository_ok() throws Exception {
-        setAuthenticatedUserToRoot();
-
-        CreateRepositoryRequest request = new CreateRepositoryRequest();
-        request.architectureCode = "x86";
-        request.code = "integrationtest";
-        request.url = "http://www.somewhere.co.nz";;
-
-        // ------------------------------------
-        repositoryApi.createRepository(request);
-        // ------------------------------------
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<Repository> repositoryAfterOptional = Repository.getByCode(context,"integrationtest"); - Assertions.assertThat(repositoryAfterOptional.get().getActive()).isTrue(); - Assertions.assertThat(repositoryAfterOptional.get().getArchitecture().getCode()).isEqualTo("x86"); - Assertions.assertThat(repositoryAfterOptional.get().getCode()).isEqualTo("integrationtest"); - Assertions.assertThat(repositoryAfterOptional.get().getUrl()).isEqualTo("http://www.somewhere.co.nz";);
-    }
-
-    @Test
-    public void testCreateRepository_codeNotUnique() throws Exception {
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        setAuthenticatedUserToRoot();
-
-        CreateRepositoryRequest request = new CreateRepositoryRequest();
-        request.architectureCode = "x86";
-        request.code = data.repository.getCode();
-        request.url = "http://www.somewhere.co.nz";;
-
-        try {
-            // ------------------------------------
-            repositoryApi.createRepository(request);
-            // ------------------------------------
-
- Assert.fail("the repository should not have been able to be created against an already existing repository code");
-        }
-        catch(ValidationException ve) {
- Assertions.assertThat(ve.getValidationFailures().size()).isEqualTo(1); - Assertions.assertThat(ve.getValidationFailures().get(0).getMessage()).isEqualTo("unique"); - Assertions.assertThat(ve.getValidationFailures().get(0).getProperty()).isEqualTo(Repository.CODE_PROPERTY);
-        }
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/UserApiIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.api1;
-
-import com.google.common.base.Optional;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.api1.UserApi;
-import org.haikuos.haikudepotserver.api1.model.user.*;
-import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
-import org.haikuos.haikudepotserver.captcha.CaptchaService;
-import org.haikuos.haikudepotserver.captcha.model.Captcha;
-import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
-import org.haikuos.haikudepotserver.dataobjects.User;
-import org.haikuos.haikudepotserver.security.AuthenticationService;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.util.Collections;
-
-public class UserApiIT extends AbstractIntegrationTest {
-
-    @Resource
-    UserApi userApi;
-
-    @Resource
-    CaptchaService captchaService;
-
-    @Resource
-    AuthenticationService authenticationService;
-
- private User createBasicUser(ObjectContext context, String nickname, String password) {
-        User user = context.newObject(User.class);
-        user.setNickname(nickname);
-        user.setPasswordSalt(); // random
- user.setPasswordHash(authenticationService.hashPassword(user, password)); - user.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get());
-        context.commitChanges();
-        return user;
-    }
-
-    @Test
-    public void testUpdateUser() throws Exception {
-
-        {
-            ObjectContext context = serverRuntime.getContext();
- User user = createBasicUser(context, "testuser", "yUe4o2Nwe009"); // language is english
-            setAuthenticatedUser("testuser");
-        }
-
-        UpdateUserRequest request = new UpdateUserRequest();
-        request.nickname = "testuser";
- request.filter = Collections.singletonList(UpdateUserRequest.Filter.NATURALLANGUAGE);
-        request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN;
-
-        // ------------------------------------
-        userApi.updateUser(request);
-        // ------------------------------------
-
-        {
-            ObjectContext context = serverRuntime.getContext();
-            Optional<User> user = User.getByNickname(context, "testuser");
- Assertions.assertThat(user.get().getNaturalLanguage().getCode()).isEqualTo(NaturalLanguage.CODE_GERMAN);
-        }
-
-    }
-
-    @Test
-    public void testCreateUser() throws Exception {
-
-        Captcha captcha = captchaService.generate();
-        CreateUserRequest request = new CreateUserRequest();
-        request.captchaToken = captcha.getToken();
-        request.captchaResponse = captcha.getResponse();
-        request.nickname = "testuser";
-        request.passwordClear = "Ue4nI92Rw";
-        request.naturalLanguageCode = "en";
-
-        // ------------------------------------
-        CreateUserResult result = userApi.createUser(request);
-        // ------------------------------------
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<User> userOptional = User.getByNickname(context,"testuser");
-
-        Assertions.assertThat(userOptional.isPresent()).isTrue();
-        Assertions.assertThat(userOptional.get().getActive()).isTrue();
-        Assertions.assertThat(userOptional.get().getIsRoot()).isFalse();
- Assertions.assertThat(userOptional.get().getNickname()).isEqualTo("testuser"); - Assertions.assertThat(userOptional.get().getNaturalLanguage().getCode()).isEqualTo("en");
-
- Assertions.assertThat(authenticationService.authenticate("testuser","Ue4nI92Rw").get()).isEqualTo(userOptional.get().getObjectId());
-    }
-
-    @Test
-    public void testGetUser_found() throws ObjectNotFoundException {
-
-        ObjectContext context = serverRuntime.getContext();
-        User user = createBasicUser(context,"testuser","yUe4o2Nwe009");
-        setAuthenticatedUser("testuser");
-
-        // ------------------------------------
- GetUserResult result = userApi.getUser(new GetUserRequest("testuser"));
-        // ------------------------------------
-
-        Assertions.assertThat(result.nickname).isEqualTo("testuser");
-        // more to come here in time
-
-    }
-
-    @Test
-         public void testAuthenticateUser_succcess() {
-
-        ObjectContext context = serverRuntime.getContext();
-        User user = createBasicUser(context, "testuser", "U7vqpsu6BB");
-        setAuthenticatedUser("testuser");
-
-        // ------------------------------------
- AuthenticateUserResult result = userApi.authenticateUser(new AuthenticateUserRequest("testuser","U7vqpsu6BB"));
-        // ------------------------------------
-
-        Assertions.assertThat(result.authenticated).isTrue();
-
-    }
-
-    @Test
-    public void testAuthenticateUser_fail() {
-
-        ObjectContext context = serverRuntime.getContext();
-        User user = createBasicUser(context,"testuser","U7vqpsu6BB");
-        setAuthenticatedUser("testuser");
-
-        // ------------------------------------
- AuthenticateUserResult result = userApi.authenticateUser(new AuthenticateUserRequest("testuser","y63j20f22"));
-        // ------------------------------------
-
-        Assertions.assertThat(result.authenticated).isFalse();
-    }
-
-    @Test
-    public void testChangePassword() throws ObjectNotFoundException {
-
-        Captcha captcha = captchaService.generate();
-        ObjectContext context = serverRuntime.getContext();
-        User user = createBasicUser(context,"testuser","U7vqpsu6BB");
-        setAuthenticatedUser("testuser");
-
-        // check that the password is correctly configured.
- Assertions.assertThat(authenticationService.authenticate("testuser","U7vqpsu6BB").get()).isEqualTo(user.getObjectId());
-
-        // now change it.
-        ChangePasswordRequest request = new ChangePasswordRequest();
-        request.nickname = "testuser";
-        request.captchaResponse = captcha.getResponse();
-        request.captchaToken = captcha.getToken();
-        request.newPasswordClear = "8R3nlp11gX";
-        request.oldPasswordClear = "U7vqpsu6BB";
-
-        // ------------------------------------
-        userApi.changePassword(request);
-        // ------------------------------------
-
- // now check that the old authentication no longer works and the new one does work - Assertions.assertThat(authenticationService.authenticate("testuser","U7vqpsu6BB").isPresent()).isFalse(); - Assertions.assertThat(authenticationService.authenticate("testuser","8R3nlp11gX").get()).isEqualTo(user.getObjectId());
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/PkgOrchestrationServiceIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.haikuos.haikudepotsever.pkg;
-
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.ObjectId;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.dataobjects.Architecture;
-import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
-import org.haikuos.haikudepotserver.dataobjects.PkgVersionLocalization;
-import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService;
-import org.haikuos.haikudepotserver.pkg.model.PkgRepositoryImportJob;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.haikuos.pkg.model.Pkg;
-import org.haikuos.pkg.model.PkgArchitecture;
-import org.haikuos.pkg.model.PkgVersion;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.util.Collections;
-
-public class PkgOrchestrationServiceIT extends AbstractIntegrationTest {
-
-    @Resource
-    PkgOrchestrationService pkgOrchestrationService;
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    private void importTestPackageWithMinor(String minor) {
-        ObjectContext context = serverRuntime.getContext();
- Repository repository = Repository.getByCode(context, "testrepository").get();
-
-        org.haikuos.pkg.model.Pkg pkg = new Pkg();
-        pkg.setArchitecture(PkgArchitecture.X86);
-        pkg.setDescription("test-description-en");
-        pkg.setSummary("test-summary-en");
-        pkg.setName("testpkg");
-        pkg.setVersion(new PkgVersion("1", minor, "3", "4", 5));
-
-        pkgOrchestrationService.importFrom(
-                context,
-                repository.getObjectId(),
-                pkg);
-
-        context.commitChanges();
-    }
-
-    /**
- * <p>Given a series of imported packages, the system will try to replicate any localizations sensibly. This test
-     * will check to make sure this works.</p>
-     */
-
-    @Test
-    public void testImportFrom_replicateLocalizationIfEnglishMatches() {
-
-        // create the repository
-
-        integrationTestSupportService.createStandardTestData();
-
-        // import the first version
-
-        importTestPackageWithMinor("2");
-
-        // now configure another language.
-
-        {
-            ObjectContext context = serverRuntime.getContext();
- org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); - org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
-                    context,
-                    pkg,
-                    Collections.singletonList(
-                            Architecture.getByCode(context, "x86").get()
-                    )).get();
-
-            pkgOrchestrationService.updatePkgVersionLocalization(
-                            context,
-                            pkgVersion,
- NaturalLanguage.getByCode(context, NaturalLanguage.CODE_GERMAN).get(),
-                            "test-summary-de",
-                            "test-description-de");
-
-            context.commitChanges();
-        }
-
-        // now load the next package version in
-
-        importTestPackageWithMinor("3");
-
-        // the second package should have the same german localization
-
-        {
-            ObjectContext context = serverRuntime.getContext();
- org.haikuos.haikudepotserver.dataobjects.Pkg pkg = org.haikuos.haikudepotserver.dataobjects.Pkg.getByName(context,"testpkg").get(); - org.haikuos.haikudepotserver.dataobjects.PkgVersion pkgVersion = org.haikuos.haikudepotserver.dataobjects.PkgVersion.getLatestForPkg(
-                    context,
-                    pkg,
-                    Collections.singletonList(
-                            Architecture.getByCode(context, "x86").get()
-                    )).get();
-
-            Assertions.assertThat(pkgVersion.getMajor()).isEqualTo("1");
-            Assertions.assertThat(pkgVersion.getMinor()).isEqualTo("3");
-
- PkgVersionLocalization localization = pkgVersion.getPkgVersionLocalization(NaturalLanguage.CODE_GERMAN).get();
-
- Assertions.assertThat(localization.getSummary()).isEqualTo("test-summary-de"); - Assertions.assertThat(localization.getDescription()).isEqualTo("test-description-de");
-        }
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgIconControllerIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.pkg.controller;
-
-import com.google.common.net.MediaType;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.pkg.controller.PkgIconController;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletResponse;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-
-public class PkgIconControllerIT extends AbstractIntegrationTest {
-
-    @Resource
-    PkgIconController pkgIconController;
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    private byte[] getIconData() throws IOException {
-        return getResourceData("/sample-32x32.png");
-    }
-
-    /**
- * <p>This test works knowing that the test package pkg1 will have a PNG image pre-loaded for it.</p>
-     */
-
-    @Test
-    public void testGet() throws Exception {
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        byte[] imageData = getIconData();
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-
-        // ------------------------------------
-        pkgIconController.handleGet(
-                response,
-                32,
-                "png",
-                "pkg1",
-                true);
-        // -----------------------------------
-
- Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); - Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/pkg/controller/PkgScreenshotControllerIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.pkg.controller;
-
-import com.google.common.base.Optional;
-import com.google.common.net.MediaType;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.dataobjects.PkgScreenshot;
-import org.haikuos.haikudepotserver.pkg.controller.PkgScreenshotController;
-import org.haikuos.haikudepotserver.support.ImageHelper;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.haikuos.haikudepotsever.IntegrationTestSupportService;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-
-public class PkgScreenshotControllerIT extends AbstractIntegrationTest {
-
-    @Resource
-    PkgScreenshotController pkgScreenshotController;
-
-    @Resource
-    IntegrationTestSupportService integrationTestSupportService;
-
-    /**
- * <p>This will return an image that can be used as a sample screenshot.</p>
-     */
-
-    private byte[] getScreenshotData() throws IOException {
-        return getResourceData("/sample-320x240.png");
-    }
-
-    @Test
-    public void testPut() throws Exception {
-
-        setAuthenticatedUserToRoot();
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        byte[] imageData = getScreenshotData();
-
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        request.setContent(imageData);
-
-        // ------------------------------------
- pkgScreenshotController.handleAdd(request, response, "png", "pkg1");
-        // -----------------------------------
-
-        // the header should contain the image code
- String code = response.getHeader(PkgScreenshotController.HEADER_SCREENSHOTCODE);
-        Assertions.assertThat(code).isNotEmpty();
-
-        ObjectContext context = serverRuntime.getContext();
- Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, code);
-        Assertions.assertThat(screenshotOptional.isPresent()).isTrue();
- Assertions.assertThat(screenshotOptional.get().getPkg().getName()).isEqualTo("pkg1"); - Assertions.assertThat(screenshotOptional.get().getPkgScreenshotImage().get().getData()).isEqualTo(imageData);
-
-    }
-
-    @Test
-    public void testGet_noScaling() throws Exception {
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        byte[] imageData = getScreenshotData();
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-
-        // ------------------------------------
-        pkgScreenshotController.handleGet(
-                response,
-                640, 480,
-                "png",
-                data.pkg1.getSortedPkgScreenshots().get(0).getCode());
-        // -----------------------------------
-
- Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString()); - Assertions.assertThat(response.getContentAsByteArray()).isEqualTo(imageData);
-
-    }
-
-    @Test
-    public void testGet_scaling() throws Exception {
-
- IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
-        byte[] imageData = getScreenshotData();
-
-        MockHttpServletResponse response = new MockHttpServletResponse();
-
-        // ------------------------------------
-        pkgScreenshotController.handleGet(
-                response,
-                160, 120,
-                "png",
-                data.pkg1.getSortedPkgScreenshots().get(0).getCode());
-        // -----------------------------------
-
- Assertions.assertThat(response.getContentType()).isEqualTo(MediaType.PNG.toString());
-
-        ImageHelper imageHelper = new ImageHelper();
- ImageHelper.Size size = imageHelper.derivePngSize(response.getContentAsByteArray());
-        Assertions.assertThat(size.width).isEqualTo(160);
-        Assertions.assertThat(size.height).isEqualTo(120);
-
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/repository/RepositoryImportServiceIT.java Mon Apr 7 11:46:03 2014 UTC
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.repository;
-
-import com.google.common.base.Optional;
-import com.google.common.io.Files;
-import org.apache.cayenne.ObjectContext;
-import org.fest.assertions.Assertions;
-import org.haikuos.haikudepotserver.dataobjects.Architecture;
-import org.haikuos.haikudepotserver.dataobjects.Pkg;
-import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotserver.pkg.model.PkgRepositoryImportJob;
-import org.haikuos.haikudepotserver.repository.RepositoryImportService;
-import org.haikuos.haikudepotsever.AbstractIntegrationTest;
-import org.junit.Test;
-
-import javax.annotation.Resource;
-import java.io.File;
-
-/**
- * <p>This test will load in a fake repository HPKR file and will then check to see that it imported correctly.</p>
- */
-
-public class RepositoryImportServiceIT extends AbstractIntegrationTest {
-
- public final static long DELAY_PROCESSSUBMITTEDTESTJOB = 60 * 1000; // 60s
-
-    @Resource
-    RepositoryImportService repositoryImportService;
-
-    private void verifyPackage(
-            ObjectContext context,
-            String name) {
-        Optional<Pkg> pkgOptional = Pkg.getByName(context, name);
-        Assertions.assertThat(pkgOptional.isPresent()).isTrue();
-    }
-
-    @Test
-    public void testImportThenCheck() throws Exception {
-
-        File temporaryFile = null;
-
-        try {
- temporaryFile = File.createTempFile("haikudepotserver-test-",".hpkr");
-
- // get the test hpkr data and copy it into a temporary file that can be used as a source
-            // for a repository.
-
- Files.write(getResourceData("/sample-repo.hpkr"), temporaryFile);
-
- // first setup a fake repository to import that points at the local test HPKR file.
-
-            {
-                ObjectContext context = serverRuntime.getContext();
- Repository repository = context.newObject(Repository.class);
-                repository.setActive(Boolean.TRUE);
-                repository.setCode("test");
- repository.setUrl("file://" + temporaryFile.getAbsolutePath()); - repository.setArchitecture(Architecture.getByCode(context, "x86").get());
-                context.commitChanges();
-            }
-
-            // do the import.
-
- repositoryImportService.submit(new PkgRepositoryImportJob("test"));
-
-            // wait for it to finish.
-
-            {
-                long startMs = System.currentTimeMillis();
-                while(
-                        repositoryImportService.isProcessingSubmittedJobs()
- && (System.currentTimeMillis() - startMs) < DELAY_PROCESSSUBMITTEDTESTJOB);
-
-                if(repositoryImportService.isProcessingSubmittedJobs()) {
- throw new IllegalStateException("test processing of the sample repo has taken > "+DELAY_PROCESSSUBMITTEDTESTJOB+"ms");
-                }
-            }
-
- // now pull out some known packages and make sure they are imported correctly. - // TODO - this is a fairly simplistic test; do some more checks.
-
-            {
-                ObjectContext context = serverRuntime.getContext();
-                verifyPackage(context,"apr");
-                verifyPackage(context,"zlib_x86_gcc2_devel");
-            }
-        }
-        finally {
-            if(null!=temporaryFile) {
-                temporaryFile.delete();
-            }
-        }
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/support/ImageHelperTest.java Mon Feb 24 08:20:27 2014 UTC
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2013-2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotsever.support;
-
-import com.google.common.io.ByteStreams;
-import org.haikuos.haikudepotserver.support.Closeables;
-import org.haikuos.haikudepotserver.support.ImageHelper;
-import org.junit.Test;
-import static org.fest.assertions.Assertions.assertThat;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class ImageHelperTest {
-
-    private byte[] getData(String leafname) throws IOException {
-        InputStream inputStream = null;
-
-        try {
-            inputStream = this.getClass().getResourceAsStream(leafname);
-            return ByteStreams.toByteArray(inputStream);
-        }
-        finally {
-            Closeables.closeQuietly(inputStream);
-        }
-    }
-
- private void assertImageSize(String leafname, int width, int height) throws IOException {
-        byte[] png = getData(leafname);
-        ImageHelper imageHelper = new ImageHelper();
-        ImageHelper.Size size = imageHelper.derivePngSize(png);
-        assertThat(size).isNotNull();
-        assertThat(size.width).isEqualTo(width);
-        assertThat(size.height).isEqualTo(height);
-    }
-
-    @Test
-    public void testDerivePngSize() throws IOException {
-        assertImageSize("/sample-260x16.png",260,16);
-        assertImageSize("/sample-32x32.png",32,32);
-        assertImageSize("/sample-16x16.png",16,16);
-    }
-
-    @Test
- public void testLooksLikeHaikuVectorImageFormat_true() throws IOException {
-         byte[] data = getData("/sample.hvif");
-        ImageHelper imageHelper = new ImageHelper();
- assertThat(imageHelper.looksLikeHaikuVectorIconFormat(data)).isTrue();
-    }
-
-    @Test
- public void testLooksLikeHaikuVectorImageFormat_false() throws IOException {
-        byte[] data = getData("/sample-16x16.png");
-        ImageHelper imageHelper = new ImageHelper();
- assertThat(imageHelper.looksLikeHaikuVectorIconFormat(data)).isFalse();
-    }
-
-}
=======================================
--- /haikudepotserver-webapp/src/test/resources/spring/test.xml Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/test/resources/spring/test.xml Wed Apr 9 11:01:59 2014 UTC
@@ -4,7 +4,7 @@
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd";>

- <bean class="org.haikuos.haikudepotsever.IntegrationTestSupportService"> + <bean class="org.haikuos.haikudepotserver.IntegrationTestSupportService">
     </bean>

 </beans>

Other related posts:

  • » [haiku-depot-web] [haiku-depot-web-app] 6 new revisions pushed by haiku.li...@xxxxxxxxx on 2014-04-09 11:05 GMT - haiku-depot-web-app