/*
 * EliasDB
 *
 * Copyright 2016 Matthias Ladkau. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package v1

import (
	"testing"
)

func TestQueryPagination(t *testing.T) {
	queryURL := "http://localhost" + TESTPORT + EndpointQuery

	st, _, res := sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)&offset=p&limit=2", "GET", nil)
	if st != "400 Bad Request" || res != "Invalid parameter value: offset should be a positive integer number" {
		t.Error("Unexpected response:", st, res)
		return
	}

	st, _, res = sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)&offset=2&limit=p", "GET", nil)
	if st != "400 Bad Request" || res != "Invalid parameter value: limit should be a positive integer number" {
		t.Error("Unexpected response:", st, res)
		return
	}

	st, h, res := sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)&offset=2&limit=3", "GET", nil)

	if st != "200 OK" || res != `
{
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [
    [
      "Aria3",
      "Aria3",
      4
    ],
    [
      "Aria4",
      "Aria4",
      18
    ],
    [
      "DeadSong2",
      "DeadSong2",
      6
    ]
  ],
  "selections": [
    false,
    false,
    false
  ],
  "sources": [
    [
      "n:Song:Aria3",
      "n:Song:Aria3",
      "n:Song:Aria3"
    ],
    [
      "n:Song:Aria4",
      "n:Song:Aria4",
      "n:Song:Aria4"
    ],
    [
      "n:Song:DeadSong2",
      "n:Song:DeadSong2",
      "n:Song:DeadSong2"
    ]
  ],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}

	// Check header values

	if tc := h.Get(HTTPHeaderTotalCount); tc != "9" {
		t.Error("Unexpected total count:", tc)
		return
	}

	rid := h.Get(HTTPHeaderCacheID)

	if _, ok := ResultCache.Get(rid); !ok {
		t.Error("Given result id should be in the cache")
		return
	}

	st, _, res = sendTestRequest(queryURL+"//main?rid="+rid+"&offset=5&limit=0", "GET", nil)
	if st != "200 OK" || res != `
{
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [],
  "selections": [],
  "sources": [],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}

	st, _, res = sendTestRequest(queryURL+"//main?rid="+rid+"&offset=5&limit=1", "GET", nil)
	if st != "200 OK" || res != `
{
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [
    [
      "FightSong4",
      "FightSong4",
      3
    ]
  ],
  "selections": [
    false
  ],
  "sources": [
    [
      "n:Song:FightSong4",
      "n:Song:FightSong4",
      "n:Song:FightSong4"
    ]
  ],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}

	st, _, res = sendTestRequest(queryURL+"//main?rid="+rid, "GET", nil)
	if st != "200 OK" || res != `
{
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [
    [
      "Aria1",
      "Aria1",
      8
    ],
    [
      "Aria2",
      "Aria2",
      2
    ],
    [
      "Aria3",
      "Aria3",
      4
    ],
    [
      "Aria4",
      "Aria4",
      18
    ],
    [
      "DeadSong2",
      "DeadSong2",
      6
    ],
    [
      "FightSong4",
      "FightSong4",
      3
    ],
    [
      "LoveSong3",
      "LoveSong3",
      1
    ],
    [
      "MyOnlySong3",
      "MyOnlySong3",
      19
    ],
    [
      "StrangeSong1",
      "StrangeSong1",
      5
    ]
  ],
  "selections": [
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false
  ],
  "sources": [
    [
      "n:Song:Aria1",
      "n:Song:Aria1",
      "n:Song:Aria1"
    ],
    [
      "n:Song:Aria2",
      "n:Song:Aria2",
      "n:Song:Aria2"
    ],
    [
      "n:Song:Aria3",
      "n:Song:Aria3",
      "n:Song:Aria3"
    ],
    [
      "n:Song:Aria4",
      "n:Song:Aria4",
      "n:Song:Aria4"
    ],
    [
      "n:Song:DeadSong2",
      "n:Song:DeadSong2",
      "n:Song:DeadSong2"
    ],
    [
      "n:Song:FightSong4",
      "n:Song:FightSong4",
      "n:Song:FightSong4"
    ],
    [
      "n:Song:LoveSong3",
      "n:Song:LoveSong3",
      "n:Song:LoveSong3"
    ],
    [
      "n:Song:MyOnlySong3",
      "n:Song:MyOnlySong3",
      "n:Song:MyOnlySong3"
    ],
    [
      "n:Song:StrangeSong1",
      "n:Song:StrangeSong1",
      "n:Song:StrangeSong1"
    ]
  ],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}

	_, _, res = sendTestRequest(queryURL+"//main?rid="+rid+"&offset=500&limit=1", "GET", nil)

	if res != "Offset exceeds available rows" {
		t.Error("Unexpected response:", res)
		return
	}

	_, _, res = sendTestRequest(queryURL+"//main?rid=abc&offset=5&limit=1", "GET", nil)

	if res != "Unknown result ID (rid parameter)" {
		t.Error("Unexpected response:", res)
		return
	}
}

func TestQuery(t *testing.T) {
	queryURL := "http://localhost" + TESTPORT + EndpointQuery

	// POST requests should not be allowed

	_, _, res := sendTestRequest(queryURL, "POST",
		[]byte(`{"msg":"Hello!"}`))

	if res != "Method Not Allowed" {
		t.Error("Unexpected response:", res)
		return
	}

	// Test error message

	_, _, res = sendTestRequest(queryURL+"main", "GET", nil)

	if res != "Missing query (q parameter)" {
		t.Error("Unexpected response:", res)
		return
	}

	_, _, res = sendTestRequest(queryURL+"main/bla/bla?q=get+Song", "GET", nil)

	if res != "Invalid resource specification: bla/bla" {
		t.Error("Unexpected response:", res)
		return
	}

	_, _, res = sendTestRequest(queryURL+"/?q=get+Song", "GET", nil)

	if res != "Need a partition" {
		t.Error("Unexpected response:", res)
		return
	}

	_, _, res = sendTestRequest(queryURL+"main/?q=get+BLA", "GET", nil)

	if res != "EQL error in Main query: Unknown node kind (BLA) (Line:1 Pos:5)" {
		t.Error("Unexpected response:", res)
		return
	}

	// Test first real query

	_, _, res = sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)", "GET", nil)
	st, _, res2 := sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)&offset=0&limit=9", "GET", nil)

	if st != "200 OK" || res2 != res || res != `
{
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [
    [
      "Aria1",
      "Aria1",
      8
    ],
    [
      "Aria2",
      "Aria2",
      2
    ],
    [
      "Aria3",
      "Aria3",
      4
    ],
    [
      "Aria4",
      "Aria4",
      18
    ],
    [
      "DeadSong2",
      "DeadSong2",
      6
    ],
    [
      "FightSong4",
      "FightSong4",
      3
    ],
    [
      "LoveSong3",
      "LoveSong3",
      1
    ],
    [
      "MyOnlySong3",
      "MyOnlySong3",
      19
    ],
    [
      "StrangeSong1",
      "StrangeSong1",
      5
    ]
  ],
  "selections": [
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false
  ],
  "sources": [
    [
      "n:Song:Aria1",
      "n:Song:Aria1",
      "n:Song:Aria1"
    ],
    [
      "n:Song:Aria2",
      "n:Song:Aria2",
      "n:Song:Aria2"
    ],
    [
      "n:Song:Aria3",
      "n:Song:Aria3",
      "n:Song:Aria3"
    ],
    [
      "n:Song:Aria4",
      "n:Song:Aria4",
      "n:Song:Aria4"
    ],
    [
      "n:Song:DeadSong2",
      "n:Song:DeadSong2",
      "n:Song:DeadSong2"
    ],
    [
      "n:Song:FightSong4",
      "n:Song:FightSong4",
      "n:Song:FightSong4"
    ],
    [
      "n:Song:LoveSong3",
      "n:Song:LoveSong3",
      "n:Song:LoveSong3"
    ],
    [
      "n:Song:MyOnlySong3",
      "n:Song:MyOnlySong3",
      "n:Song:MyOnlySong3"
    ],
    [
      "n:Song:StrangeSong1",
      "n:Song:StrangeSong1",
      "n:Song:StrangeSong1"
    ]
  ],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}
}

func TestGroupingInfo(t *testing.T) {
	queryURL := "http://localhost" + TESTPORT + EndpointQuery

	st, _, res := sendTestRequest(queryURL+"//main?q=get+Song+with+ordering(ascending+key)&offset=2&limit=3&groups=1", "GET", nil)

	if st != "200 OK" || res != `
{
  "groups": [
    [
      "Best"
    ],
    [],
    []
  ],
  "header": {
    "data": [
      "1:n:key",
      "1:n:name",
      "1:n:ranking"
    ],
    "format": [
      "auto",
      "auto",
      "auto"
    ],
    "labels": [
      "Song Key",
      "Song Name",
      "Ranking"
    ],
    "primary_kind": "Song"
  },
  "rows": [
    [
      "Aria3",
      "Aria3",
      4
    ],
    [
      "Aria4",
      "Aria4",
      18
    ],
    [
      "DeadSong2",
      "DeadSong2",
      6
    ]
  ],
  "selections": [
    false,
    false,
    false
  ],
  "sources": [
    [
      "n:Song:Aria3",
      "n:Song:Aria3",
      "n:Song:Aria3"
    ],
    [
      "n:Song:Aria4",
      "n:Song:Aria4",
      "n:Song:Aria4"
    ],
    [
      "n:Song:DeadSong2",
      "n:Song:DeadSong2",
      "n:Song:DeadSong2"
    ]
  ],
  "total_selections": 0
}`[1:] {
		t.Error("Unexpected response:", st, res)
		return
	}

	st, _, res = sendTestRequest(queryURL+"//main?q=get+Song+primary+'group'+with+ordering(ascending+key)&offset=2&limit=3&groups=1", "GET", nil)

	if st != "400 Bad Request" || res != "Could not determine key of primary node - query needs a primary expression" {
		t.Error("Unexpected response:", st, res)
		return
	}
}
