diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml
new file mode 100644
index 000000000..dbd435939
--- /dev/null
+++ b/models/fixtures/issue.yml
@@ -0,0 +1,46 @@
+-
+  id: 1
+  repo_id: 1
+  index: 1
+  poster_id: 1
+  name: issue1
+  content: content1
+  is_closed: false
+  is_pull: false
+  created_unix: 946684800
+  updated_unix: 978307200
+
+-
+  id: 2
+  repo_id: 1
+  index: 2
+  poster_id: 1
+  name: issue2
+  content: content2
+  is_closed: false
+  is_pull: true
+  created_unix: 946684810
+  updated_unix: 978307190
+
+
+-
+  id: 3
+  repo_id: 1
+  index: 3
+  poster_id: 1
+  name: issue3
+  content: content4
+  is_closed: false
+  is_pull: true
+  created_unix: 946684820
+  updated_unix: 978307180
+
+-
+  id: 4
+  repo_id: 2
+  index: 1
+  poster_id: 2
+  name: issue4
+  content: content4
+  is_closed: true
+  is_pull: false
diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml
new file mode 100644
index 000000000..34de6693e
--- /dev/null
+++ b/models/fixtures/pull_request.yml
@@ -0,0 +1,28 @@
+-
+  id: 1
+  type: 0 # gitea pull request
+  status: 2 # mergable
+  issue_id: 2
+  index: 1
+  head_repo_id: 1
+  base_repo_id: 1
+  head_user_name: user1
+  head_branch: branch1
+  base_branch: master
+  merge_base: 1234567890abcdef
+  has_merged: true
+  merger_id: 2
+
+-
+  id: 2
+  type: 0 # gitea pull request
+  status: 1 # checking
+  issue_id: 3
+  index: 2
+  head_repo_id: 1
+  base_repo_id: 1
+  head_user_name: user1
+  head_branch: branch2
+  base_branch: master
+  merge_base: fedcba9876543210
+  has_merged: false
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
new file mode 100644
index 000000000..21d9184d1
--- /dev/null
+++ b/models/fixtures/repository.yml
@@ -0,0 +1,21 @@
+-
+  id: 1
+  owner_id: 2
+  lower_name: repo1
+  name: repo1
+  is_private: false
+  num_issues: 1
+  num_closed_issues: 0
+  num_pulls: 2
+  num_closed_pulls: 0
+
+-
+  id: 2
+  owner_id: 2
+  lower_name: repo2
+  name: repo2
+  is_private: true
+  num_issues: 1
+  num_closed_issues: 1
+  num_pulls: 0
+  num_closed_pulls: 0
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
new file mode 100644
index 000000000..8847fc7aa
--- /dev/null
+++ b/models/fixtures/user.yml
@@ -0,0 +1,25 @@
+- # NOTE: this user (id=1) is the admin
+  id: 1
+  lower_name: user1
+  name: user1
+  email: user1@example.com
+  passwd: password
+  type: 0 # individual
+  salt: salt
+  is_admin: true
+  avatar: avatar1
+  avatar_email: user2@example.com
+  num_repos: 0
+
+-
+  id: 2
+  lower_name: user2
+  name: user2
+  email: user2@example.com
+  passwd: password
+  type: 0 # individual
+  salt: salt
+  is_admin: false
+  avatar: avatar2
+  avatar_email: user2@example.com
+  num_repos: 2
diff --git a/models/issue.go b/models/issue.go
index b8f23d594..c0377e634 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -876,6 +876,27 @@ type IssuesOptions struct {
 	SortType    string
 }
 
+// sortIssuesSession sort an issues-related session based on the provided
+// sortType string
+func sortIssuesSession(sess *xorm.Session, sortType string) {
+	switch sortType {
+	case "oldest":
+		sess.Asc("issue.created_unix")
+	case "recentupdate":
+		sess.Desc("issue.updated_unix")
+	case "leastupdate":
+		sess.Asc("issue.updated_unix")
+	case "mostcomment":
+		sess.Desc("issue.num_comments")
+	case "leastcomment":
+		sess.Asc("issue.num_comments")
+	case "priority":
+		sess.Desc("issue.priority")
+	default:
+		sess.Desc("issue.created_unix")
+	}
+}
+
 // Issues returns a list of issues by given conditions.
 func Issues(opts *IssuesOptions) ([]*Issue, error) {
 	if opts.Page <= 0 {
@@ -912,22 +933,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
 
 	sess.And("issue.is_pull=?", opts.IsPull)
 
-	switch opts.SortType {
-	case "oldest":
-		sess.Asc("issue.created_unix")
-	case "recentupdate":
-		sess.Desc("issue.updated_unix")
-	case "leastupdate":
-		sess.Asc("issue.updated_unix")
-	case "mostcomment":
-		sess.Desc("issue.num_comments")
-	case "leastcomment":
-		sess.Asc("issue.num_comments")
-	case "priority":
-		sess.Desc("issue.priority")
-	default:
-		sess.Desc("issue.created_unix")
-	}
+	sortIssuesSession(sess, opts.SortType)
 
 	if len(opts.Labels) > 0 && opts.Labels != "0" {
 		labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
diff --git a/models/pull.go b/models/pull.go
index 8661fa3aa..9beab4df6 100644
--- a/models/pull.go
+++ b/models/pull.go
@@ -21,6 +21,7 @@ import (
 	api "code.gitea.io/sdk/gitea"
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/xorm"
+	"code.gitea.io/gitea/modules/base"
 )
 
 var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
@@ -542,7 +543,7 @@ type PullRequestsOptions struct {
 	MilestoneID int64
 }
 
-func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
+func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) (*xorm.Session, error) {
 	sess := x.Where("pull_request.base_repo_id=?", baseRepoID)
 
 	sess.Join("INNER", "issue", "pull_request.issue_id = issue.id")
@@ -551,7 +552,20 @@ func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) *xorm
 		sess.And("issue.is_closed=?", opts.State == "closed")
 	}
 
-	return sess
+	sortIssuesSession(sess, opts.SortType)
+
+	if labelIDs, err := base.StringsToInt64s(opts.Labels); err != nil {
+		return nil, err
+	} else if len(labelIDs) > 0 {
+		sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
+			In("issue_label.label_id", labelIDs)
+	}
+
+	if opts.MilestoneID > 0 {
+		sess.And("issue.milestone_id=?", opts.MilestoneID)
+	}
+
+	return sess, nil
 }
 
 // PullRequests returns all pull requests for a base Repo by the given conditions
@@ -560,7 +574,11 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest,
 		opts.Page = 1
 	}
 
-	countSession := listPullRequestStatement(baseRepoID, opts)
+	countSession, err := listPullRequestStatement(baseRepoID, opts)
+	if err != nil {
+		log.Error(4, "listPullRequestStatement", err)
+		return nil, 0, err
+	}
 	maxResults, err := countSession.Count(new(PullRequest))
 	if err != nil {
 		log.Error(4, "Count PRs", err)
@@ -568,7 +586,11 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest,
 	}
 
 	prs := make([]*PullRequest, 0, ItemsPerPage)
-	findSession := listPullRequestStatement(baseRepoID, opts)
+	findSession, err := listPullRequestStatement(baseRepoID, opts)
+	if err != nil {
+		log.Error(4, "listPullRequestStatement", err)
+		return nil, maxResults, err
+	}
 	findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage)
 	return prs, maxResults, findSession.Find(&prs)
 }
@@ -624,7 +646,7 @@ func GetPullRequestByIndex(repoID int64, index int64) (*PullRequest, error) {
 	if err != nil {
 		return nil, err
 	} else if !has {
-		return nil, ErrPullRequestNotExist{0, repoID, index, 0, "", ""}
+		return nil, ErrPullRequestNotExist{0, 0, 0, repoID, "", ""}
 	}
 
 	if err = pr.LoadAttributes(); err != nil {
diff --git a/models/pull_test.go b/models/pull_test.go
new file mode 100644
index 000000000..2cfc9f772
--- /dev/null
+++ b/models/pull_test.go
@@ -0,0 +1,251 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// getPullRequest load a fixture pull request from the test database
+func loadFixturePullRequest(t *testing.T, id int64) *PullRequest {
+	sess := x.NewSession()
+	defer sess.Close()
+
+	pr := &PullRequest{ID: id}
+	has, err := sess.Get(pr)
+	assert.NoError(t, err)
+	assert.True(t, has)
+	return pr
+}
+
+func TestPullRequest_LoadAttributes(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := loadFixturePullRequest(t, 1)
+	assert.NoError(t, pr.LoadAttributes())
+	assert.NotNil(t, pr.Merger)
+	assert.Equal(t, pr.MergerID, pr.Merger.ID)
+}
+
+func TestPullRequest_LoadIssue(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := loadFixturePullRequest(t, 1)
+	assert.NoError(t, pr.LoadIssue())
+	assert.NotNil(t, pr.Issue)
+	assert.Equal(t, int64(2), pr.Issue.ID)
+}
+
+// TODO TestPullRequest_APIFormat
+
+func TestPullRequest_GetBaseRepo(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := loadFixturePullRequest(t, 1)
+	assert.NoError(t, pr.GetBaseRepo())
+	assert.NotNil(t, pr.BaseRepo)
+	assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
+}
+
+func TestPullRequest_GetHeadRepo(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := loadFixturePullRequest(t, 1)
+	assert.NoError(t, pr.GetHeadRepo())
+	assert.NotNil(t, pr.HeadRepo)
+	assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID)
+}
+
+// TODO TestMerge
+
+// TODO TestNewPullRequest
+
+func TestPullRequestsNewest(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	prs, count, err := PullRequests(1, &PullRequestsOptions{
+		Page:     1,
+		State:    "open",
+		SortType: "newest",
+		Labels:   []string{},
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, int64(2), count)
+	assert.Len(t, prs, 2)
+	assert.Equal(t, int64(2), prs[0].ID)
+	assert.Equal(t, int64(1), prs[1].ID)
+}
+
+func TestPullRequestsOldest(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	prs, count, err := PullRequests(1, &PullRequestsOptions{
+		Page:     1,
+		State:    "open",
+		SortType: "oldest",
+		Labels:   []string{},
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, int64(2), count)
+	assert.Len(t, prs, 2)
+	assert.Equal(t, int64(1), prs[0].ID)
+	assert.Equal(t, int64(2), prs[1].ID)
+}
+
+func TestGetUnmergedPullRequest(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr, err := GetUnmergedPullRequest(1, 1, "branch2", "master")
+	assert.NoError(t, err)
+	assert.Equal(t, int64(2), pr.ID)
+
+	pr, err = GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master")
+	assert.Error(t, err)
+	assert.True(t, IsErrPullRequestNotExist(err))
+}
+
+func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	prs, err := GetUnmergedPullRequestsByHeadInfo(1, "branch2")
+	assert.NoError(t, err)
+	assert.Len(t, prs, 1)
+	for _, pr := range prs {
+		assert.Equal(t, int64(1), pr.HeadRepoID)
+		assert.Equal(t, "branch2", pr.HeadBranch)
+	}
+}
+
+func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	prs, err := GetUnmergedPullRequestsByBaseInfo(1, "master")
+	assert.NoError(t, err)
+	assert.Len(t, prs, 1)
+	pr := prs[0]
+	assert.Equal(t, int64(2), pr.ID)
+	assert.Equal(t, int64(1), pr.BaseRepoID)
+	assert.Equal(t, "master", pr.BaseBranch)
+}
+
+func TestGetPullRequestByIndex(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr, err := GetPullRequestByIndex(1, 2)
+	assert.NoError(t, err)
+	assert.Equal(t, int64(1), pr.BaseRepoID)
+	assert.Equal(t, int64(2), pr.Index)
+
+	pr, err = GetPullRequestByIndex(9223372036854775807, 9223372036854775807)
+	assert.Error(t, err)
+	assert.True(t, IsErrPullRequestNotExist(err))
+}
+
+func TestGetPullRequestByID(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr, err := GetPullRequestByID(1)
+	assert.NoError(t, err)
+	assert.Equal(t, int64(1), pr.ID)
+	assert.Equal(t, int64(2), pr.IssueID)
+
+	_, err = GetPullRequestByID(9223372036854775807)
+	assert.Error(t, err)
+	assert.True(t, IsErrPullRequestNotExist(err))
+}
+
+func TestGetPullRequestByIssueID(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr, err := GetPullRequestByIssueID(2)
+	assert.NoError(t, err)
+	assert.Equal(t, int64(2), pr.IssueID)
+
+	pr, err = GetPullRequestByIssueID(9223372036854775807)
+	assert.Error(t, err)
+	assert.True(t, IsErrPullRequestNotExist(err))
+}
+
+func TestPullRequest_Update(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := &PullRequest{
+		ID:         1,
+		IssueID:    100,
+		BaseBranch: "baseBranch",
+		HeadBranch: "headBranch",
+	}
+	pr.Update()
+
+	sess := x.NewSession()
+	defer sess.Close()
+	pr = &PullRequest{ID: 1}
+	has, err := sess.Get(pr)
+	assert.NoError(t, err)
+	assert.True(t, has)
+	assert.Equal(t, int64(100), pr.IssueID)
+	assert.Equal(t, "baseBranch", pr.BaseBranch)
+	assert.Equal(t, "headBranch", pr.HeadBranch)
+}
+
+func TestPullRequest_UpdateCols(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	pr := &PullRequest{
+		ID:         1,
+		IssueID:    int64(100),
+		BaseBranch: "baseBranch",
+		HeadBranch: "headBranch",
+	}
+	pr.UpdateCols("issue_id", "head_branch")
+
+	sess := x.NewSession()
+	defer sess.Close()
+	pr = &PullRequest{ID: 1}
+	has, err := sess.Get(pr)
+	assert.NoError(t, err)
+	assert.True(t, has)
+	assert.Equal(t, int64(100), pr.IssueID)
+	assert.Equal(t, "master", pr.BaseBranch)
+	assert.Equal(t, "headBranch", pr.HeadBranch)
+}
+
+// TODO TestPullRequest_UpdatePatch
+
+// TODO TestPullRequest_PushToBaseRepo
+
+func TestPullRequest_AddToTaskQueue(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+
+	pr := loadFixturePullRequest(t, 1)
+	pr.AddToTaskQueue()
+
+	// briefly sleep so that background threads have time to run
+	time.Sleep(time.Millisecond)
+
+	assert.True(t, pullRequestQueue.Exist(pr.ID))
+	pr = loadFixturePullRequest(t, 1)
+	assert.Equal(t, PullRequestStatusChecking, pr.Status)
+}
+
+func TestPullRequestList_LoadAttributes(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+
+	prs := []*PullRequest{
+		loadFixturePullRequest(t, 1),
+		loadFixturePullRequest(t, 2),
+	}
+	assert.NoError(t, PullRequestList(prs).LoadAttributes())
+	for _, pr := range prs {
+		assert.NotNil(t, pr.Issue)
+		assert.Equal(t, pr.IssueID, pr.Issue.ID)
+	}
+}
+
+// TODO TestAddTestPullRequestTask
+
+func TestChangeUsernameInPullRequests(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	const newUsername = "newusername"
+	assert.NoError(t, ChangeUsernameInPullRequests("user1", newUsername))
+	sess := x.NewSession()
+	defer sess.Close()
+
+	prs := make([]*PullRequest, 0, 10)
+	assert.NoError(t, sess.Where("head_user_name = ?", newUsername).Find(&prs))
+	assert.Len(t, prs, 2)
+	for _, pr := range prs {
+		assert.Equal(t, newUsername, pr.HeadUserName)
+	}
+}
diff --git a/models/repo.go b/models/repo.go
index 4acb0d736..5f1cef302 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -27,6 +27,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/sync"
 	api "code.gitea.io/sdk/gitea"
+
 	"github.com/Unknwon/cae/zip"
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/xorm"