Repository: netlify/gocommerce (go)

Issue:
Placing an order with multiple digital products allows to download only files for the first added

**- Do you want to request a *feature* or report a *bug*?**
Bug

**- What is the current behavior?**
Placing an order with multiple digital products allows to download only files for the first added

**- If the current behavior is a bug, please provide the steps to reproduce.**
Put an order with more than a single digital product with downloads associated. Placing such order with multiple digital products allows to download only files for the first added

**- What is the expected behavior?**
Placing an order with multiple digital products allows to download all files from all line items

+ @mraerino 

Pull request:
Fix downloads being overwritten when having multiple items
*Fixes #191*

**- Summary**

There was a bug where downloads from the last line item would overwrite other downloads in an order. This is fixed.

**- Test plan**

Added regression test.

**- Description for the changelog**

Fix bug with missing downloads on orders with multiple items

**- A picture of a cute animal (not mandatory but encouraged)**

![penguin baby](https://pmcvariety.files.wordpress.com/2019/03/disneynature-penguins.jpg)

Patch:
diff --git a/models/line_item.go b/models/line_item.go
index 50ed44b..80ab3ac 100644
--- a/models/line_item.go
+++ b/models/line_item.go
@@ -285,7 +285,7 @@ func (i *LineItem) Process(config *conf.Configuration, userClaims map[string]int
 		i.AddonItems[index].Price = lowestPrice.cents
 	}
 
-	order.Downloads = i.MissingDownloads(order, meta)
+	order.Downloads = append(order.Downloads, i.MissingDownloads(order, meta)...)
 
 	return i.calculatePrice(userClaims, meta.Prices, order.Currency)
 }
@@ -338,10 +338,10 @@ func (i *LineItem) FetchMeta(siteURL string) (*LineItemMetadata, error) {
 // MissingDownloads returns all downloads that are not yet listed in the order
 func (i *LineItem) MissingDownloads(order *Order, meta *LineItemMetadata) []Download {
 	downloads := []Download{}
-	for _, download := range meta.Downloads {
+	for _, metaDownload := range meta.Downloads {
 		alreadyCreated := false
 		for _, d := range order.Downloads {
-			if d.URL == download.URL {
+			if d.URL == metaDownload.URL {
 				alreadyCreated = true
 				break
 			}
@@ -349,11 +349,14 @@ func (i *LineItem) MissingDownloads(order *Order, meta *LineItemMetadata) []Down
 		if alreadyCreated {
 			continue
 		}
-		download.ID = uuid.NewRandom().String()
-		download.OrderID = order.ID
-		download.Title = i.Title
-		download.Sku = i.Sku
-		downloads = append(downloads, download)
+		orderDownload := metaDownload
+		orderDownload.ID = uuid.NewRandom().String()
+		orderDownload.OrderID = order.ID
+		orderDownload.Sku = i.Sku
+		if orderDownload.Title == "" {
+			orderDownload.Title = i.Title
+		}
+		downloads = append(downloads, orderDownload)
 	}
 	return downloads
 }


Test patch:
diff --git a/api/order_test.go b/api/order_test.go
index 9d56e1c..31790fc 100644
--- a/api/order_test.go
+++ b/api/order_test.go
@@ -217,7 +217,7 @@ func TestOrderCreate(t *testing.T) {
 					Claims: map[string]string{
 						"email": test.Data.testUser.Email,
 					},
-					Percentage: 15,
+					Percentage:   15,
 					ProductTypes: []string{"Book"},
 				},
 			},
@@ -249,6 +249,68 @@ func TestOrderCreate(t *testing.T) {
 		assert.Equal(t, uint64(15), discountItem.Percentage)
 		assert.Equal(t, uint64(0), discountItem.Fixed)
 	})
+
+	t.Run("MultipleItemsWithDownloads", func(t *testing.T) {
+		test := NewRouteTest(t)
+
+		site := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			switch r.URL.Path {
+			case "/i/believe/i/can/fly":
+				fmt.Fprint(w, productMetaFrame(
+					`{
+						"sku": "123-i-can-fly-456",
+						"downloads": [{"title": "First Download", "url": "/assets/first-download"}],
+						"prices": [{"currency": "USD", "amount": "3.00"}]
+					}`,
+				))
+				return
+			case "/its/not/about/the/money":
+				fmt.Fprintf(w, productMetaFrame(
+					`{
+						"sku": "not-about-the-money",
+						"downloads": [{"title": "Second Download", "url": "/assets/second-download"}],
+						"prices": [{"currency": "USD", "amount": "5.00"}]
+					}`,
+				))
+				return
+			}
+			w.WriteHeader(http.StatusNotFound)
+		}))
+		defer site.Close()
+		test.Config.SiteURL = site.URL
+
+		body := strings.NewReader(`{
+			"email": "info@example.com",
+			"shipping_address": {
+				"name": "Test User",
+				"address1": "Branengebranen",
+				"city": "Berlin", "country": "Germany", "zip": "94107"
+			},
+			"line_items": [
+				{"path": "/i/believe/i/can/fly", "quantity": 1},
+				{"path": "/its/not/about/the/money", "quantity": 1}
+			]
+		}`)
+		token := test.Data.testUserToken
+		recorder := test.TestEndpoint(http.MethodPost, "/orders", body, token)
+
+		order := &models.Order{}
+		extractPayload(t, http.StatusCreated, recorder, order)
+		assert.Len(t, order.Downloads, 2)
+		for _, dl := range order.Downloads {
+			fmt.Printf("dl: %+v\n", dl)
+			switch dl.Sku {
+			case "123-i-can-fly-456":
+				assert.Equal(t, "First Download", dl.Title)
+				assert.Equal(t, "/assets/first-download", dl.URL)
+			case "not-about-the-money":
+				assert.Equal(t, "Second Download", dl.Title)
+				assert.Equal(t, "/assets/second-download", dl.URL)
+			default:
+				t.Errorf("Unknown download item: %+v", dl)
+			}
+		}
+	})
 }
 
 func TestOrderCreateNewUser(t *testing.T) {