/* Tests internal URIs generated by various methods in the code base.
 * If you manually generate a news URI somewhere, please add it to this test.
 */

/* import-globals-from ../../../test/resources/logHelper.js */
/* import-globals-from ../../../test/resources/asyncTestUtils.js */
/* import-globals-from ../../../test/resources/alertTestUtils.js */
load("../../../resources/logHelper.js");
load("../../../resources/asyncTestUtils.js");
load("../../../resources/alertTestUtils.js");

var {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");

var dummyMsgWindow = {
  get statusFeedback() {
    return {
      startMeteors() {},
      stopMeteors() {
        async_driver();
      },
      showProgress() {},
    };
  },
  get promptDialog() {
    return alertUtilsPrompts;
  },
  QueryInterface: ChromeUtils.generateQI([Ci.nsIMsgWindow,
                                          Ci.nsISupportsWeakReference]),
};
var daemon, localserver, server;

var tests = [
  test_newMsgs,
  test_cancel,
  test_fetchMessage,
  test_search,
  test_grouplist,
  test_postMessage,
  test_escapedName,
  cleanUp,
];

var kCancelArticle =
  "From: fake@acme.invalid\n" +
  "Newsgroups: test.filter\n" +
  "Subject: cancel <4@regular.invalid>\n" +
  "References: <4@regular.invalid>\n" +
  "Control: cancel <4@regular.invalid>\n" +
  "MIME-Version: 1.0\n" +
  "Content-Type: text/plain\n" +
  "\n" +
  "This message was cancelled from within ";

function* test_newMsgs() {
  // This tests nsMsgNewsFolder::GetNewsMessages via getNewMessages
  let folder = localserver.rootFolder.getChildNamed("test.filter");
  Assert.equal(folder.getTotalMessages(false), 0);
  folder.getNewMessages(null, asyncUrlListener);
  yield false;
  Assert.equal(folder.getTotalMessages(false), 8);
  yield true;
}

/* exported alert, confirmEx */
// Prompts for cancel
function alert(title, text) {}
function confirmEx(title, text, flags) { return 0; }

function* test_cancel() {
  // This tests nsMsgNewsFolder::CancelMessage
  let folder = localserver.rootFolder.getChildNamed("test.filter");
  let db = folder.msgDatabase;
  let hdr = db.GetMsgHdrForKey(4);

  let folderListener = {
    OnItemEvent(aEventFolder, aEvent) {
      if (aEvent == "DeleteOrMoveMsgCompleted") {
        MailServices.mailSession.RemoveFolderListener(this);
      }
    },
  };
  MailServices.mailSession.AddFolderListener(folderListener, Ci.nsIFolderListener.event);
  folder.QueryInterface(Ci.nsIMsgNewsFolder)
        .cancelMessage(hdr, dummyMsgWindow);
  yield false;

  Assert.equal(folder.getTotalMessages(false), 7);

  // Check the content of the CancelMessage itself.
  let article = daemon.getGroup("test.filter")[9];
  // Since the cancel message includes the brand name (Daily, Thunderbird), we
  // only check the beginning of the string.
  Assert.ok(article.fullText.startsWith(kCancelArticle));
  yield true;
}

function* test_fetchMessage() {
  // Tests nsNntpService::CreateMessageIDURL via FetchMessage
  var statuscode = -1;
  let streamlistener = {
    onDataAvailable() {},
    onStartRequest() {},
    onStopRequest(aRequest, aStatus) {
      statuscode = aStatus;
    },
    QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener,
                                            Ci.nsIRequestObserver]),
  };
  let folder = localserver.rootFolder.getChildNamed("test.filter");
  MailServices.nntp.fetchMessage(folder, 2, null, streamlistener, asyncUrlListener);
  yield false;
  Assert.equal(statuscode, Cr.NS_OK);
  yield true;
}

function* test_search() {
  // This tests nsNntpService::Search
  let folder = localserver.rootFolder.getChildNamed("test.filter");
  var searchSession = Cc["@mozilla.org/messenger/searchSession;1"]
                        .createInstance(Ci.nsIMsgSearchSession);
  searchSession.addScopeTerm(Ci.nsMsgSearchScope.news, folder);

  let searchTerm = searchSession.createTerm();
  searchTerm.attrib = Ci.nsMsgSearchAttrib.Subject;
  let value = searchTerm.value;
  value.str = "First";
  searchTerm.value = value;
  searchTerm.op = Ci.nsMsgSearchOp.Contains;
  searchTerm.booleanAnd = false;
  searchSession.appendTerm(searchTerm);

  let hitCount;
  let searchListener = {
    onSearchHit() { hitCount++; },
    onSearchDone() {
      searchSession.unregisterListener(this);
      async_driver();
    },
    onNewSearch() { hitCount = 0; },
  };
  searchSession.registerListener(searchListener);

  searchSession.search(null);
  yield false;

  Assert.equal(hitCount, 1);
  yield true;
}

function* test_grouplist() {
  // This tests nsNntpService::GetListOfGroupsOnServer
  let subserver = localserver.QueryInterface(Ci.nsISubscribableServer);
  let subscribeListener = {
    OnDonePopulating() { async_driver(); },
  };
  subserver.subscribeListener = subscribeListener;

  function enumGroups(rootUri) {
    let hierarchy = subserver.getChildURIs(rootUri);
    let groups = [];
    for (let name of hierarchy) {
      if (subserver.isSubscribable(name))
        groups.push(name);
      if (subserver.hasChildren(name))
        groups = groups.concat(enumGroups(name));
    }
    return groups;
  }

  MailServices.nntp.getListOfGroupsOnServer(localserver, null, false);
  yield false;

  let groups = enumGroups("");
  Assert.equal(groups.length, Object.keys(daemon._groups).length);
  for (let group in daemon._groups) {
    Assert.ok(groups.includes(group));
  }

  // First node in the group list, even though it is not subscribable,
  // parent of "test.empty" group.
  Assert.equal(subserver.getFirstChildURI(""), "test");

  // Release reference, somehow impedes GC of 'subserver'.
  subserver.subscribeListener = null;
  yield true;
}

function* test_postMessage() {
  // This tests nsNntpService::SetUpNntpUrlForPosting via PostMessage
  MailServices.nntp.postMessage(do_get_file("postings/post2.eml"), "misc.test",
    localserver.key, asyncUrlListener, null);
  yield false;
  Assert.equal(daemon.getGroup("misc.test").keys.length, 1);
  yield true;
}

// Not tested because it requires UI, and this is insufficient, I think.
// function test_forwardInline() {
//   // This tests mime_parse_stream_complete via forwarding inline
//   let folder = localserver.rootFolder.getChildNamed("test.filter");
//   let hdr = folder.msgDatabase.GetMsgHdrForKey(1);
//   MailServices.compose.forwardMessage("a@b.invalid", hdr, null,
//     localserver, Ci.nsIMsgComposeService.kForwardInline);
// }

function* test_escapedName() {
  // This does a few tests to make sure our internal URIs work for newsgroups
  // with names that need escaping
  let evilName = "test.malformed&name";
  daemon.addGroup(evilName);
  daemon.addArticle(make_article(do_get_file("postings/bug670935.eml")));
  localserver.subscribeToNewsgroup(evilName);

  // Can we access it?
  let folder = localserver.rootFolder.getChildNamed(evilName);
  folder.getNewMessages(null, asyncUrlListener);
  yield false;

  // If we get here, we didn't crash--newsgroups unescape properly.
  // Load a message, to test news-message: URI unescaping
  var statuscode = -1;
  let streamlistener = {
    onDataAvailable() {},
    onStartRequest() {},
    onStopRequest(aRequest, aStatus) {
      statuscode = aStatus;
    },
    QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener,
                                            Ci.nsIRequestObserver]),
  };
  MailServices.nntp.fetchMessage(folder, 1, null, streamlistener, asyncUrlListener);
  yield false;
  Assert.equal(statuscode, Cr.NS_OK);
  yield true;
}

function run_test() {
  daemon = setupNNTPDaemon();
  server = makeServer(NNTP_RFC2980_handler, daemon);
  server.start();
  localserver = setupLocalServer(server.port);

  // Set up an identity for posting
  let identity = MailServices.accounts.createIdentity();
  identity.fullName = "Normal Person";
  identity.email = "fake@acme.invalid";
  MailServices.accounts.FindAccountForServer(localserver).addIdentity(identity);

  async_run_tests(tests);
}

function cleanUp() {
  localserver.closeCachedConnections();
}
