|
7 | 7 | "net/http"
|
8 | 8 | "net/http/httptest"
|
9 | 9 | "os"
|
| 10 | + "strconv" |
10 | 11 | "strings"
|
11 | 12 | "testing"
|
12 | 13 | "time"
|
@@ -533,6 +534,159 @@ func TestRest_FindUserComments_CWE_918(t *testing.T) {
|
533 | 534 | assert.Equal(t, arbitraryServer.URL, resp.Comments[0].Locator.URL, "arbitrary URL provided by the request")
|
534 | 535 | }
|
535 | 536 |
|
| 537 | +func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) { |
| 538 | + // test that comment counting is consistent between tree and plain formats |
| 539 | + ts, srv, teardown := startupT(t) |
| 540 | + defer teardown() |
| 541 | + |
| 542 | + commentLocator := store.Locator{URL: "test-url", SiteID: "remark42"} |
| 543 | + |
| 544 | + // vote for comment multiple times |
| 545 | + setScore := func(locator store.Locator, id string, val int) { |
| 546 | + abs := func(x int) int { |
| 547 | + if x < 0 { |
| 548 | + return -x |
| 549 | + } |
| 550 | + return x |
| 551 | + } |
| 552 | + for i := 0; i < abs(val); i++ { |
| 553 | + _, err := srv.DataService.Vote(service.VoteReq{ |
| 554 | + Locator: locator, |
| 555 | + CommentID: id, |
| 556 | + // unique user ID is needed for correct counting of controversial votes |
| 557 | + UserID: "user" + strconv.Itoa(val) + strconv.Itoa(i), |
| 558 | + Val: val > 0, |
| 559 | + }) |
| 560 | + require.NoError(t, err) |
| 561 | + } |
| 562 | + } |
| 563 | + |
| 564 | + // Adding initial comments (8 to test-url and 1 to another-url) and voting, and delete two of comments to the first post. |
| 565 | + // With sleep so that at least few millisecond pass between each comment |
| 566 | + // and later we would be able to use that in "since" filter with millisecond precision |
| 567 | + ids := make([]string, 9) |
| 568 | + timestamps := make([]time.Time, 9) |
| 569 | + c1 := store.Comment{Text: "top-level comment 1", Locator: commentLocator} |
| 570 | + ids[0], timestamps[0] = addCommentGetCreatedTime(t, c1, ts) |
| 571 | + // #3 by score |
| 572 | + setScore(commentLocator, ids[0], 1) |
| 573 | + time.Sleep(time.Millisecond * 5) |
| 574 | + |
| 575 | + c2 := store.Comment{Text: "top-level comment 2", Locator: commentLocator} |
| 576 | + ids[1], timestamps[1] = addCommentGetCreatedTime(t, c2, ts) |
| 577 | + // #2 by score |
| 578 | + setScore(commentLocator, ids[1], 2) |
| 579 | + time.Sleep(time.Millisecond * 5) |
| 580 | + |
| 581 | + c3 := store.Comment{Text: "second-level comment 1", ParentID: ids[0], Locator: commentLocator} |
| 582 | + ids[2], timestamps[2] = addCommentGetCreatedTime(t, c3, ts) |
| 583 | + // #1 by score |
| 584 | + setScore(commentLocator, ids[2], 10) |
| 585 | + time.Sleep(time.Millisecond * 5) |
| 586 | + |
| 587 | + c4 := store.Comment{Text: "third-level comment 1", ParentID: ids[2], Locator: commentLocator} |
| 588 | + ids[3], timestamps[3] = addCommentGetCreatedTime(t, c4, ts) |
| 589 | + // #5 by score, #1 by controversy |
| 590 | + setScore(commentLocator, ids[3], 4) |
| 591 | + setScore(commentLocator, ids[3], -4) |
| 592 | + time.Sleep(time.Millisecond * 5) |
| 593 | + |
| 594 | + c5 := store.Comment{Text: "second-level comment 2", ParentID: ids[1], Locator: commentLocator} |
| 595 | + ids[4], timestamps[4] = addCommentGetCreatedTime(t, c5, ts) |
| 596 | + // #5 by score, #2 by controversy |
| 597 | + setScore(commentLocator, ids[4], 2) |
| 598 | + setScore(commentLocator, ids[4], -3) |
| 599 | + time.Sleep(time.Millisecond * 5) |
| 600 | + |
| 601 | + c6 := store.Comment{Text: "third-level comment 2", ParentID: ids[4], Locator: commentLocator} |
| 602 | + ids[5], timestamps[5] = addCommentGetCreatedTime(t, c6, ts) |
| 603 | + // deleted later so not visible in site-wide requests |
| 604 | + setScore(commentLocator, ids[5], 10) |
| 605 | + setScore(commentLocator, ids[5], -10) |
| 606 | + time.Sleep(time.Millisecond * 5) |
| 607 | + |
| 608 | + c7 := store.Comment{Text: "top-level comment 3", Locator: commentLocator} |
| 609 | + ids[6], timestamps[6] = addCommentGetCreatedTime(t, c7, ts) |
| 610 | + // #6 by score, #4 by controversy |
| 611 | + setScore(commentLocator, ids[6], -3) |
| 612 | + setScore(commentLocator, ids[6], 1) |
| 613 | + time.Sleep(time.Millisecond * 5) |
| 614 | + |
| 615 | + c8 := store.Comment{Text: "second-level comment 3", ParentID: ids[6], Locator: commentLocator} |
| 616 | + ids[7], timestamps[7] = addCommentGetCreatedTime(t, c8, ts) |
| 617 | + // deleted later so not visible in site-wide requests |
| 618 | + setScore(commentLocator, ids[7], -20) |
| 619 | + |
| 620 | + c9 := store.Comment{Text: "comment to post 2", Locator: store.Locator{URL: "another-url", SiteID: "remark42"}} |
| 621 | + ids[8], timestamps[8] = addCommentGetCreatedTime(t, c9, ts) |
| 622 | + // #7 by score |
| 623 | + setScore(store.Locator{URL: "another-url", SiteID: "remark42"}, ids[8], -25) |
| 624 | + |
| 625 | + // delete two comments bringing the total from 9 to 6 |
| 626 | + err := srv.DataService.Delete(commentLocator, ids[7], store.SoftDelete) |
| 627 | + assert.NoError(t, err) |
| 628 | + err = srv.DataService.Delete(commentLocator, ids[5], store.HardDelete) |
| 629 | + assert.NoError(t, err) |
| 630 | + srv.Cache.Flush(cache.FlusherRequest{}) |
| 631 | + |
| 632 | + sinceTenSecondsAgo := strconv.FormatInt(time.Now().Add(-time.Second*10).UnixNano()/1000000, 10) |
| 633 | + sinceTS := make([]string, 9) |
| 634 | + formattedTS := make([]string, 9) |
| 635 | + for i, created := range timestamps { |
| 636 | + sinceTS[i] = strconv.FormatInt(created.UnixNano()/1000000, 10) |
| 637 | + formattedTS[i] = created.Format(time.RFC3339Nano) |
| 638 | + } |
| 639 | + t.Logf("last timestamp: %v", timestamps[7]) |
| 640 | + |
| 641 | + testCases := []struct { |
| 642 | + params string |
| 643 | + expectedBody string |
| 644 | + }{ |
| 645 | + {"", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 646 | + {"url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 647 | + {"format=plain", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 648 | + {"format=plain&url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 649 | + {"since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 650 | + {"url=test-url&since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 651 | + {"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 652 | + {"url=test-url&since=" + sinceTS[0], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 653 | + {"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 654 | + {"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 655 | + {"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])}, |
| 656 | + {"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])}, |
| 657 | + {"format=tree", `"info":{"url":"test-url","count":7`}, |
| 658 | + {"format=tree&url=test-url", `"info":{"url":"test-url","count":6`}, |
| 659 | + {"format=tree&sort=+time", `"info":{"url":"test-url","count":7`}, |
| 660 | + {"format=tree&url=test-url&sort=+time", `"info":{"url":"test-url","count":6`}, |
| 661 | + {"format=tree&sort=-score", `"info":{"url":"test-url","count":7`}, |
| 662 | + {"format=tree&url=test-url&sort=-score", `"info":{"url":"test-url","count":6`}, |
| 663 | + {"sort=+time", fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])}, |
| 664 | + {"sort=-time", fmt.Sprintf(`"score":1,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[0])}, |
| 665 | + {"sort=+score", fmt.Sprintf(`"score":10,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[2])}, |
| 666 | + {"sort=+score&url=test-url", fmt.Sprintf(`"score":10,"vote":0,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[2])}, |
| 667 | + {"sort=-score", fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])}, |
| 668 | + {"sort=-score&url=test-url", fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])}, |
| 669 | + {"sort=-time&since=" + sinceTS[4], fmt.Sprintf(`"score":-1,"vote":0,"controversy":2.924017738212866,"time":%q}],"info":{"count":7`, formattedTS[4])}, |
| 670 | + {"sort=-score&since=" + sinceTS[3], fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])}, |
| 671 | + {"sort=-score&url=test-url&since=" + sinceTS[3], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])}, |
| 672 | + {"sort=+controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])}, |
| 673 | + // three comments of which last one deleted and doesn't have controversy so returned last |
| 674 | + {"sort=-controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":0,"vote":0,"time":%q,"delete":true}],"info":{"url":"test-url","count":6`, formattedTS[7])}, |
| 675 | + } |
| 676 | + |
| 677 | + for _, tc := range testCases { |
| 678 | + t.Run(tc.params, func(t *testing.T) { |
| 679 | + url := fmt.Sprintf(ts.URL+"/api/v1/find?site=remark42&%s", tc.params) |
| 680 | + body, code := get(t, url) |
| 681 | + assert.Equal(t, http.StatusOK, code) |
| 682 | + assert.Contains(t, body, tc.expectedBody) |
| 683 | + t.Log(body) |
| 684 | + // prevent hit limiter from engaging |
| 685 | + time.Sleep(50 * time.Millisecond) |
| 686 | + }) |
| 687 | + } |
| 688 | +} |
| 689 | + |
536 | 690 | func TestRest_UserInfo(t *testing.T) {
|
537 | 691 | ts, _, teardown := startupT(t)
|
538 | 692 | defer teardown()
|
|
0 commit comments