/// <summary>
 /// Cleans up resources when tests are finished.
 /// </summary>
 public void Dispose()
 {
     AFFixture.Dispose();
     PIFixture.Dispose();
     Client.Dispose();
     ServicePointManager.ServerCertificateValidationCallback = null;
 }
        public void CheckAFServiceTest()
        {
            var service = "AFService";

            using (var fixture = new AFFixture())
            {
                Utils.CheckServiceRunning(fixture.PISystem.ConnectionInfo.Host, service, Output);
            }
        }
        internal static void GetWebApiClient(ref WebClient client, ref AFElement configElement, ref bool disableWrites, ref bool anonymousAuthentication)
        {
            PISystem pisys = AFFixture.GetPISystemFromConfig();

            if (client == null)
            {
                client = new WebClient {
                    UseDefaultCredentials = true
                }
            }
            ;

            client.Headers.Add("X-Requested-With", "XMLHttpRequest");

            ServicePointManager.ServerCertificateValidationCallback = null;
            if (Settings.SkipCertificateValidation)
#pragma warning disable CA5359 // Do Not Disable Certificate Validation
            {
                ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
            }
#pragma warning restore CA5359 // Do Not Disable Certificate Validation

            var configurationElement = Settings.PIWebAPI.Split('.')[0];
            if (!string.IsNullOrEmpty(Settings.PIWebAPIConfigurationInstance))
            {
                configurationElement = Settings.PIWebAPIConfigurationInstance;
            }

            var path    = $"\\\\{Settings.AFServer}\\Configuration\\OSIsoft\\PI Web API\\{configurationElement}\\System Configuration";
            var results = AFElement.FindElementsByPath(new string[] { path }, pisys);
            configElement = results.FirstOrDefault();
            if (!Equals(configElement, null))
            {
                disableWrites = (bool)configElement.Attributes["DisableWrites"].GetValue().Value;
                var methods = (string[])configElement.Attributes["AuthenticationMethods"].GetValue().Value;
                if (methods.Length > 0)
                {
                    if (string.Equals(methods[0], "Basic", StringComparison.OrdinalIgnoreCase))
                    {
                        client.UseDefaultCredentials = false;
                        var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(Settings.PIWebAPIUser + ":" + Settings.PIWebAPIPassword));
                        client.Headers[HttpRequestHeader.Authorization] = "Basic " + credentials;
                    }
                    else if (string.Equals(methods[0], "Anonymous", StringComparison.OrdinalIgnoreCase))
                    {
                        anonymousAuthentication = true;
                    }
                }
                else
                {
                    throw new InvalidOperationException("PI Web API Authentication Methods are not specified in the Configuration database.");
                }
            }
        }
    }
        public void CheckProductVersion()
        {
            Output.WriteLine($"AFSDK Version: {AF.AFGlobalSettings.SDKVersion}");
            using (var affixture = new AFFixture())
            {
                Assert.True(affixture.PISystem != null, $"AF Server [{affixture.PISystem}] could not be found.");
                Output.WriteLine($"AF Server Version: {affixture.PISystem.ServerVersion}");
            }

            using (var pifixture = new PIFixture())
            {
                Assert.True(pifixture.PIServer != null, $"PI Server [{pifixture.PIServer}] could not be found.");
                Output.WriteLine($"PI Data Archive Version: {pifixture.PIServer.ServerVersion}");
            }
        }
        public void CheckRtqpEngineServiceTest()
        {
            var service = "PISqlDas.Rtqp";

            using (var fixture = new AFFixture())
            {
                if (Settings.PISqlClientTests)
                {
                    Utils.CheckServiceRunning(fixture.PISystem.ConnectionInfo.Host, service, Output);
                }
                else
                {
                    Output.WriteLine($"'PISQLClientTests' setting value is set to 'false' or not set at all. Check if [{service}] (required by PI SQL Client) is running was skipped.");
                }
            }
        }
        internal void InitializeWebService(AFFixture afFixture, ITestOutputHelper output)
        {
            if (AFFixture != null)
            {
                return;
            }

            // Initialize a web service notification contact template
            AFFixture          = afFixture;
            Service            = new TestWebService();
            SoapWebServiceHost = new BasicWebServiceHost(Service, typeof(IWebService));

            try
            {
                SoapWebServiceHost.Start(ServiceName, ServiceUri);
            }
            catch (AddressAccessDeniedException)
            {
                SoapWebServiceHost = null;
                Service            = null;

                output.WriteLine($"Warning: The Web Service endpoint [{ServiceUri}] could not be opened.");
                output.WriteLine("There are two ways how to fix the problem:");
                output.WriteLine("1. Use netsh add urlacl to add the current user for the service prefix http://+:9001/notificationtest");
                output.WriteLine("2. Run tests as administrator.");

                return;
            }

            var webServiceSoap = new AFNotificationContactTemplate(afFixture.PISystem, $"{TestPrefix}_{TestInfix}_WebServiceSoap*")
            {
                DeliveryChannelPlugIn = afFixture.PISystem.DeliveryChannelPlugIns[WebServicePlugInName],
                ConfigString          = $"Style=SOAP;WebServiceName={ServiceName};WebServiceMethod={nameof(IWebService.Test)};WebServiceUrl={ServiceUri}",
            };

            _soapWebServiceId = webServiceSoap.ID;
            _notificationContactTemplateIds.Add(webServiceSoap.ID);

            AFFixture.PISystem.CheckIn();
            output.WriteLine($"Created web service notification contact template [{webServiceSoap.Name}].");
        }
        internal void InitializeWebService(AFFixture afFixture, ITestOutputHelper output)
        {
            if (AFFixture != null)
            {
                return;
            }

            // Initialize a web service notification contact template
            AFFixture          = afFixture;
            Service            = new TestWebService();
            SoapWebServiceHost = new BasicWebServiceHost(Service, typeof(IWebService))
            {
                UseExactMatchForLocalhost = false,
            };

            try
            {
                SoapWebServiceHost.Start(ServiceName, ServiceUri);
            }
            catch (AddressAccessDeniedException ex)
            {
                throw new Exception(
                          $"Endpoint [{ServiceUri}] could not be opened. There are 3 possible solutions: " +
                          "1) If notifications service and the tests are run on the same machine, set UseExactMatchForLocalhost=true; " +
                          "2) Use netsh add urlacl to add the current user for the service prefix http://+:9001/notificationtest; " +
                          "3) Run tests as administrator.", ex);
            }

            var webServiceSoap = new AFNotificationContactTemplate(afFixture.PISystem, $"{TestPrefix}_{TestInfix}_WebServiceSoap*")
            {
                DeliveryChannelPlugIn = afFixture.PISystem.DeliveryChannelPlugIns[WebServicePlugInName],
                ConfigString          = $"Style=SOAP;WebServiceName={ServiceName};WebServiceMethod={nameof(IWebService.Test)};WebServiceUrl={ServiceUri}",
            };

            _soapWebServiceId = webServiceSoap.ID;
            _notificationContactTemplatIds.Add(webServiceSoap.ID);

            AFFixture.PISystem.CheckIn();
            output.WriteLine($"Created web service notification contact template [{webServiceSoap.Name}].");
        }
        /// <summary>
        /// Gets the machine name of service.
        /// </summary>
        /// <param name="settingName">Name of the setting for the service in app config settings file.</param>
        /// <returns>Name of machine running that service.</returns>
        public static string GetMachineName(string settingName)
        {
            switch (settingName)
            {
            case "PIDataArchive":
                using (var pifixture = new PIFixture())
                    return(pifixture.PIServer.ConnectionInfo.Host);

            case "AFServer":
                using (var affixture = new AFFixture())
                    return(affixture.PISystem.ConnectionInfo.Host);

            case "PIAnalysisService":
                return(Settings.PIAnalysisService);

            case "PINotificationsService":
                return(Settings.PINotificationsService);

            case "PIWebAPI":
                return(Settings.PIWebAPI);

            case "PIWebAPICrawler":
                return(Settings.PIWebAPICrawler);

            case "PIVisionServer":
                if (string.IsNullOrWhiteSpace(Settings.PIVisionServer))
                {
                    return(string.Empty);
                }
                return(Settings.PIVisionServer.Split('/')[2].Split(':')[0]);

            case "PIManualLogger":
                return(Settings.PIManualLogger);

            default:
                return($"Invalid setting name '{settingName}' specified.");
            }
        }
Beispiel #9
0
        public void CheckMinimumPISQLClientSecurity()
        {
            using (var fixture = new AFFixture())
            {
                if (Settings.PISqlClientTests)
                {
                    var fullElementPath = @"OSIsoft\RTQP Engine\Custom Objects";
                    var element         = fixture.PISystem.Databases.ConfigurationDatabase?.Elements[fullElementPath];

                    if (!(element is null))
                    {
                        Assert.True(element.Security.CanRead && element.Security.CanWrite,
                                    $"The user does not have Read/Write permissions on the {fullElementPath} element in the Configuration database.");
                        Assert.True(element.Security.CanReadData && element.Security.CanWriteData,
                                    $"The user does not have Read/Write Data permissions on the {fullElementPath} element in the Configuration database.");
                        Assert.True(element.Security.CanDelete,
                                    $"The user does not have Delete permissions on the {fullElementPath} element in the Configuration database.");
                    }
                    else
                    {
                        Assert.True(false, $"Could not find element {fullElementPath} in the configuration directory.");
                    }
                }
Beispiel #10
0
        public void CheckMinimumAFSecurity()
        {
            using (var fixture = new AFFixture())
            {
                var system = fixture.PISystem;
                Assert.True(system.UOMDatabase.Security.CanWrite, "The current user must have Write permission on the UOMDatabase.");

                foreach (var securityItem in Enum.GetValues(typeof(AFSecurityItem)))
                {
                    var security = system.GetSecurity((AFSecurityItem)securityItem);

                    switch (securityItem)
                    {
                    case AFSecurityItem.AnalysisTemplate:
                    case AFSecurityItem.Category:
                    case AFSecurityItem.Database:
                    case AFSecurityItem.EnumerationSet:
                    case AFSecurityItem.NotificationContactTemplate:
                    case AFSecurityItem.NotificationRuleTemplate:
                    case AFSecurityItem.Table:
                        Assert.True(security.CanRead &&
                                    security.CanWrite &&
                                    security.CanDelete, "The current user must have Read, Write, and Delete permission to the following System collections:\n" +
                                    "\tAnalysis Templates\n" +
                                    "\tCategories\n" +
                                    "\tDatabases\n" +
                                    "\tEnumeration Sets\n" +
                                    "\tNotification Contact Templates\n" +
                                    "\tNotification Rule Templates\n" +
                                    "\tTables");
                        break;

                    case AFSecurityItem.EventFrame:
                    case AFSecurityItem.Transfer:
                        Assert.True(security.CanReadData &&
                                    security.CanWriteData &&
                                    security.CanDelete &&
                                    security.CanAnnotate, "The current user must have Read Data, Write Data, Annotate, and Delete permission to the following System collections:\n" +
                                    "\tEvent Frames\n" +
                                    "\tTransfers");
                        break;

                    case AFSecurityItem.Analysis:
                        Assert.True(security.CanRead &&
                                    security.CanWrite &&
                                    security.CanDelete &&
                                    security.CanExecute, "The current user must have Read, Write, Execute, and Delete permission to the Analyses System collection.");
                        break;

                    case AFSecurityItem.Element:
                    case AFSecurityItem.ElementTemplate:
                        Assert.True(security.CanRead &&
                                    security.CanReadData &&
                                    security.CanWrite &&
                                    security.CanWriteData &&
                                    security.CanDelete, "The current user must have Read, Write, Read Data, Write Data, and Delete permission to the following System collections:\n" +
                                    "\tElements\n" +
                                    "\tElement Templates");

                        var identities      = system.CurrentUserIdentities;
                        var instancedSystem = fixture.GetInstancedSystem();
                        if ((AFSecurityItem)securityItem == AFSecurityItem.Element)
                        {
                            Assert.True(security.CanAnnotate, "The current user must have Annotate permission to the Elements System collection.");
                        }
                        else
                        {
                            var elementTemplateToken = instancedSystem.GetSecurity(AFSecurityItem.ElementTemplate).Token;
                            elementTemplateToken.SecurityItem = AFSecurityItem.EventFrame;
                            var tokens = new List <AFSecurityRightsToken>()
                            {
                                elementTemplateToken
                            };
                            var dict = AFSecurity.CheckSecurity(instancedSystem, identities, tokens);
                            Assert.True(dict[elementTemplateToken.ObjectId].CanAnnotate(), "The current user must have Annotate permission to the Element Templates System collection.");
                        }

                        instancedSystem.Disconnect();
                        break;

                    case AFSecurityItem.NotificationRule:
                        Assert.True(security.CanRead &&
                                    security.CanWrite &&
                                    security.CanDelete &&
                                    security.CanSubscribe, "The current user must have Read, Write, Subscribe, and Delete permission to the Notification Rules System collection.");
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Skips a test based on the passed condition.
        /// </summary>
        public GenericFactAttribute(TestCondition feature, bool error)
            : base(AFTests.KeySetting, AFTests.KeySettingTypeCode)
        {
            string afVersion = AFGlobalSettings.SDKVersion;
            string piHomeDir = string.Empty;

            // Return if the Skip property has been changed in the base constructor
            if (!string.IsNullOrEmpty(Skip))
            {
                return;
            }
            string piHome64 = null;
            string piHome32 = null;

            DataLinkUtils.GetPIHOME(ref piHome64, 1);
            DataLinkUtils.GetPIHOME(ref piHome32, 0);
            switch (feature)
            {
            case TestCondition.AFCLIENTCURRENTPATCH:
                Version sdkVersion = new Version(afVersion);
                if (sdkVersion < _afClientCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_afClientCurrentVersionString}! Please consider upgrading! You are currently on {sdkVersion}";
                }

                break;

            case TestCondition.AFPATCH2107:
                sdkVersion = new Version(afVersion);
                if (sdkVersion < new Version("2.10.7"))
                {
                    Skip = $@"Warning! You do not have the critical patch: PI AF 2018 SP3 Patch 1 (2.10.7)! Please consider upgrading to avoid data loss! You are currently on {sdkVersion}";
                }

                break;

            case TestCondition.AFSERVERCURRENTPATCH:
                Version serverVersion = new Version(AFFixture.GetPISystemFromConfig().ServerVersion);
                if (serverVersion < _afServerCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_afServerCurrentVersionString}! Please consider upgrading! You are currently on {serverVersion}";
                }

                break;

            case TestCondition.ANALYSISCURRENTPATCH:
                Version analysisVer = new Version(AFFixture.GetPISystemFromConfig().AnalysisRulePlugIns["PerformanceEquation"].Version);
                if (analysisVer < _analysisCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_analysisCurrentVersionString}! Please consider upgrading! You are currently on {analysisVer}";
                }

                break;

            case TestCondition.DATALINKCURRENTPATCH:
                string afDataDLLPath = @"Excel\OSIsoft.PIDataLink.AFData.dll";

                DataLinkUtils.GetPIHOME(ref piHomeDir);
                Version dataLinkVersion = new Version(FileVersionInfo.GetVersionInfo(Path.Combine(piHomeDir, afDataDLLPath)).FileVersion);
                if (dataLinkVersion < _dataLinkCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_dataLinkCurrentVersionString}! Please consider upgrading! You are currently on {dataLinkVersion}";
                }

                break;

            case TestCondition.NOTIFICATIONSCURRENTPATCH:
                PISystem system      = AFFixture.GetPISystemFromConfig();
                var      configstore = new PINotificationsConfigurationStore(system);
                PINotificationsWCFClientManager wcfClient = new PINotificationsWCFClientManager(configstore);
                var serviceStatus = wcfClient.GetServiceStatus();
                wcfClient.Dispose();
                Version notificationsVersion = new Version(serviceStatus.Version);
                if (notificationsVersion < _notificationsCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_notificationsCurrentVersionString}! Please consider upgrading! You are currently on {notificationsVersion}";
                }

                break;

            case TestCondition.PIDACURRENTPATCH:
                PIServer serv = new PIServers()[Settings.PIDataArchive];
                serv.Connect();
                Version pidaVersion = new Version(serv.ServerVersion);
                if (pidaVersion < _dataArchiveCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_dataArchiveCurrentVersionString}! Please consider upgrading! You are currently on {pidaVersion}";
                }

                break;

            case TestCondition.PISQLOLEDBCURRENTPATCH:
                string sqlpath64OLEDB = Path.Combine(piHome64, @"SQL\SQL Client\OLEDB\PISQLOLEDB64.dll");
                string sqlpath32OLEDB = Path.Combine(piHome32, @"SQL\SQL Client\OLEDB\PISQLOLEDB.dll");
                if (File.Exists(sqlpath64OLEDB))
                {
                    Version piSqlVersion = new Version(FileVersionInfo.GetVersionInfo(sqlpath64OLEDB).FileVersion);
                    if (piSqlVersion < _sqlClientOLEDBCurrentVersion)
                    {
                        Skip = $@"Warning! You do not have the latest update: {_sqlClientOLEDBCurrentVersionString}! Please consider upgrading! You are currently on {piSqlVersion}";
                    }
                }

                if (File.Exists(sqlpath32OLEDB))
                {
                    Version piSqlVersion = new Version(FileVersionInfo.GetVersionInfo(sqlpath32OLEDB).FileVersion);
                    if (piSqlVersion < _sqlClientOLEDBCurrentVersion)
                    {
                        Skip = $@"Warning! You do not have the latest update: {_sqlClientOLEDBCurrentVersionString}! Please consider upgrading! You are currently on {piSqlVersion}";
                    }
                }

                break;

            case TestCondition.PISQLODBCCURRENTPATCH:
                string sqlpath64ODBC = Path.Combine(piHome64, @"SQL\SQL Client\ODBC\PISQLODBCB64.dll");
                string sqlpath32ODBC = Path.Combine(piHome32, @"SQL\SQL Client\ODBC\PISQLODBC.dll");
                if (File.Exists(sqlpath64ODBC))
                {
                    Version piSqlVersion = new Version(FileVersionInfo.GetVersionInfo(sqlpath64ODBC).FileVersion);
                    if (piSqlVersion < _sqlClientODBCCurrentVersion)
                    {
                        Skip = $@"Warning! You do not have the latest update: {_sqlClientODBCCurrentVersionString}! Please consider upgrading! You are currently on {piSqlVersion}";
                    }
                }

                if (File.Exists(sqlpath32ODBC))
                {
                    Version piSqlVersion = new Version(FileVersionInfo.GetVersionInfo(sqlpath32ODBC).FileVersion);
                    if (piSqlVersion < _sqlClientODBCCurrentVersion)
                    {
                        Skip = $@"Warning! You do not have the latest update: {_sqlClientODBCCurrentVersionString}! Please consider upgrading! You are currently on {piSqlVersion}";
                    }
                }

                break;

            case TestCondition.PIWEBAPICURRENTPATCH:
                var url = $"https://{Settings.PIWebAPI}:{443}/piwebapi/system";

                WebClient client = new WebClient {
                    UseDefaultCredentials = true
                };
                AFElement elem          = new AFElement();
                bool      disableWrites = false;
                bool      anonymousAuth = false;
                JObject   data          = new JObject();

                try
                {
                    PIWebAPIFixture.GetWebApiClient(ref client, ref elem, ref disableWrites, ref anonymousAuth);
                    data = JObject.Parse(client.DownloadString(url));
                }
                catch
                {
                    throw new InvalidOperationException($"Could not retrieve PI Web API version from server {Settings.PIWebAPI}!");
                }
                finally
                {
                    client.Dispose();
                }

                var     productVersion  = (string)data["ProductVersion"];
                Version piWebAPIVersion = new Version(productVersion);
                if (piWebAPIVersion < _webAPICurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_webAPICurrentVersionString}! Please consider upgrading! You are currently on {piWebAPIVersion}";
                }

                break;

            case TestCondition.RTQPCURRENTPATCH:
                string connectionString = $"Provider=PISQLClient.1;Data Source={Settings.AFDatabase};Location={Settings.AFServer};Integrated Security=SSPI;OLE DB Services=-2";
                using (var connection = new OleDbConnection(connectionString))
                {
                    connection.Open();

                    try
                    {
                        using (var command = new OleDbCommand("SELECT Version FROM System.Diagnostics.Version WHERE Item='Query Processor'", connection))
                        {
                            string  tempVersion = (string)command.ExecuteScalar();
                            Version rtqpVersion = new Version(tempVersion);
                            if (rtqpVersion < _rtqpCurrentVersion)
                            {
                                Skip = $@"Warning! You do not have the latest update: {_rtqpCurrentVersionString}! Please consider upgrading! You are currently on {rtqpVersion}";
                            }
                        }
                    }
                    catch (Exception)
                    {
                        Skip = $@"Warning! You do not have the latest update: {_rtqpCurrentVersionString}! Please consider upgrading!";
                    }
                }

                break;

            case TestCondition.PIVISIONCURRENTPATCH:
                string path = Settings.PIVisionServer;
                if (path.EndsWith("/#/", StringComparison.OrdinalIgnoreCase))
                {
                    path = path.Substring(0, path.Length - 3);
                }

                string visionUrl = $"{path}/Utility/permissions/read";

                var visionProductVersion = string.Empty;
                using (var visionClient = new WebClient {
                    UseDefaultCredentials = true
                })
                {
                    visionClient.Headers.Add("X-Requested-With", "XMLHttpRequest");
                    visionProductVersion = visionClient.DownloadString(visionUrl);
                }

                Version piVisionVersion = new Version();
                if (!string.IsNullOrWhiteSpace(visionProductVersion))
                {
                    piVisionVersion = new Version(visionProductVersion);
                }
                else
                {
                    throw new InvalidOperationException($"Could not retrieve PI Vision version from server {Settings.PIVisionServer}!");
                }

                if (piVisionVersion < _visionCurrentVersion)
                {
                    Skip = $@"Warning! You do not have the latest update: {_visionCurrentVersionString}! Please consider upgrading! You are currently on {piVisionVersion}";
                }

                break;
            }

            if (error && !string.IsNullOrEmpty(Skip))
            {
                throw new Exception(Skip);
            }
        }
        public void CreatePointSendEventsAndVerify()
        {
            string pointName = $"CreatePointSendEventsAndVerify{AFTime.Now}";

            IDictionary<string, object> attributes = new Dictionary<string, object>()
            {
                { PICommonPointAttributes.Tag, pointName },
                { PICommonPointAttributes.PointType, PIPointType.Int32 },
                { PICommonPointAttributes.Compressing, 0 },
            };

            // Create a PI Point
            Output.WriteLine($"Create PI Point [{pointName}].");
            PIPoint point = Fixture.PIServer.CreatePIPoint(pointName, attributes);

            // Assert that the PI Point creation was successful
            Assert.True(PIPoint.FindPIPoint(Fixture.PIServer, pointName) != null,
                $"Could not find PI Point [{pointName}] on Data Archive [{Fixture.PIServer.Name}].");

            try
            {
                // Prepare the events to be written
                var eventsToWrite = new AFValues();
                var start = AFTime.Now.ToPIPrecision();
                int eventsCount = 10;
                var randomData = new byte[eventsCount];
                using (var random = RandomNumberGenerator.Create())
                {
                    random.GetBytes(randomData);
                }

                for (int i = 0; i < eventsCount; i++)
                {
                    var evnt = new AFValue(Convert.ToInt32(randomData[i]), start + TimeSpan.FromSeconds(i));
                    eventsToWrite.Add(evnt);
                }

                // Send the events to be written
                Output.WriteLine($"Write events to PI Point [{pointName}].");
                point.UpdateValues(eventsToWrite, AFUpdateOption.InsertNoCompression);

                // Read the events to verify that it was written successfully
                var eventsRead = new AFValues();
                Output.WriteLine($"Read events from PI Point [{pointName}].");

                AssertEventually.Equal(
                    eventsToWrite.Count,
                    () => point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false).Count,
                    TimeSpan.FromSeconds(2),
                    TimeSpan.FromSeconds(0.2));

                eventsRead = point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false);

                for (int j = 0; j < eventsCount; j++)
                {
                    Assert.True(eventsToWrite[j].Value.Equals(eventsRead[j].Value),
                        $"Expected the value of the written event {AFFixture.DisplayAFValue(eventsToWrite[j])} " +
                        $"and the read event {AFFixture.DisplayAFValue(eventsRead[j])} to be equal.");
                }
            }
            finally
            {
                // Delete the PI Point to cleanup
                Fixture.DeletePIPoints(pointName, Output);
            }
        }
        public void UpdateEventValuesTest()
        {
            string pointName = $"UpdateEventValuesTest{AFTime.Now}";

            IDictionary<string, object> attributes = new Dictionary<string, object>()
            {
                { PICommonPointAttributes.Tag, pointName },
                { PICommonPointAttributes.PointType, PIPointType.Int32 },
                { PICommonPointAttributes.Compressing, 0 },
            };

            // Create a PI Point
            Output.WriteLine($"Create PI Point [{pointName}].");
            PIPoint point = Fixture.PIServer.CreatePIPoint(pointName, attributes);

            // Assert that the PI Point creation was successful
            Assert.True(PIPoint.FindPIPoint(Fixture.PIServer, pointName) != null,
                $"Could not find PI Point [{pointName}] on Data Archive [{Fixture.PIServer.Name}].");

            try
            {
                // Prepare the events to be written
                var eventsToWrite = new AFValues();
                var start = AFTime.Now.ToPIPrecision();
                int eventsCount = 10;
                var randomData = new byte[eventsCount];
                var valuesToWrite = new List<int>();
                var timeStamps = new List<AFTime>();

                using (var random = RandomNumberGenerator.Create())
                {
                    random.GetBytes(randomData);
                    for (int i = 0; i < eventsCount; i++)
                    {
                        int value = Convert.ToInt32(randomData[i]);
                        var timestamp = start + TimeSpan.FromMinutes(i);
                        var evnt = new AFValue(value, timestamp);
                        eventsToWrite.Add(evnt);
                        valuesToWrite.Add(value);
                        timeStamps.Add(timestamp);
                    }
                }

                // Send the events to be written
                Output.WriteLine($"Write {eventsCount} events to PI Point [{pointName}].");
                point.UpdateValues(eventsToWrite, AFUpdateOption.InsertNoCompression);
                Thread.Sleep(TimeSpan.FromSeconds(1));

                // Read the events to verify that it was written successfully
                var eventsRead = new AFValues();

                Output.WriteLine($"Read events from point [{pointName}].");
                AssertEventually.True(() =>
                {
                    eventsRead = point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false);
                    if (eventsToWrite.Count != eventsRead.Count)
                    {
                        Output.WriteLine($"The read event count {eventsRead.Count} does not match the written event count {eventsToWrite.Count}.");
                        return false;
                    }

                    for (int j = 0; j < eventsCount; j++)
                    {
                        if (!eventsToWrite[j].Value.Equals(eventsRead[j].Value))
                        {
                            Output.WriteLine($"Written event value {AFFixture.DisplayAFValue(eventsToWrite[j])} " +
                                $"did not match read event value {AFFixture.DisplayAFValue(eventsRead[j])}.");
                            return false;
                        }
                    }

                    return true;
                },
                TimeSpan.FromSeconds(30),
                TimeSpan.FromSeconds(5),
                $"The events read back do not match the events written on the PI Data Archive [{Fixture.PIServer.Name}].");

                // Update/edit the values and send again
                var updatedNewValues = new List<int>();
                var eventsUpdated = new AFValues();
                randomData = new byte[eventsCount];

                using (var random = RandomNumberGenerator.Create())
                {
                    random.GetBytes(randomData);
                    for (int i = 0; i < eventsCount; i++)
                    {
                        // Ensure that the updated values are different than the written values.
                        int value = Convert.ToInt32(randomData[i]) + 256;
                        var evnt = new AFValue(value, timeStamps[i]);
                        eventsUpdated.Add(evnt);
                        updatedNewValues.Add(value);
                    }
                }

                // Send the updated events to be written
                Output.WriteLine($"Write updated events to PI Point [{pointName}].");
                point.UpdateValues(eventsUpdated, AFUpdateOption.Replace);

                // Read the events to verify that it was updated successfully
                eventsRead = new AFValues();

                Output.WriteLine($"Read events from PI Point [{pointName}].");
                AssertEventually.True(() =>
                {
                    eventsRead = point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false);
                    if (eventsUpdated.Count != eventsRead.Count)
                    {
                        Output.WriteLine($"The read event count {eventsRead.Count} does not match the updated event count {eventsUpdated.Count}.");
                        return false;
                    }

                    for (int j = 0; j < eventsCount; j++)
                    {
                        if (!eventsUpdated[j].Value.Equals(eventsRead[j].Value))
                        {
                            Output.WriteLine($"Updated event value {AFFixture.DisplayAFValue(eventsUpdated[j])} " +
                                $"did not match read event value {AFFixture.DisplayAFValue(eventsRead[j])}.");
                            return false;
                        }

                        if (eventsToWrite[j].Value.Equals(eventsRead[j].Value))
                        {
                            Output.WriteLine($"Written event value {AFFixture.DisplayAFValue(eventsToWrite[j])} " +
                                $"did match read event value {AFFixture.DisplayAFValue(eventsRead[j])}. The values should not be equal.");
                            return false;
                        }
                    }

                    return true;
                },
                $"The events read back do not match the events written or updated on the PI Data Archive [{Fixture.PIServer.Name}].");
            }
            finally
            {
                // Delete the PI Point to cleanup
                Fixture.DeletePIPoints(pointName, Output);
            }
        }
        public void DeleteValuesTest()
        {
            string pointName = $"DeleteValuesTest{AFTime.Now}";

            IDictionary<string, object> attributes = new Dictionary<string, object>()
            {
                { PICommonPointAttributes.Tag, pointName },
                { PICommonPointAttributes.PointType, PIPointType.Int32 },
                { PICommonPointAttributes.Compressing, 0 },
            };

            // Create a PI Point
            Output.WriteLine($"Create PI Point [{pointName}].");
            PIPoint point = Fixture.PIServer.CreatePIPoint(pointName, attributes);

            // Assert that the PI Point creation was successful
            Assert.True(PIPoint.FindPIPoint(Fixture.PIServer, pointName) != null,
                $"Could not find PI Point [{pointName}] on Data Archive [{Fixture.PIServer.Name}].");

            try
            {
                // Prepare the events to be written
                var eventsToWrite = new AFValues();
                var start = AFTime.Now.ToPIPrecision();
                int eventsCount = 10;
                var randomData = new byte[eventsCount];

                using (var random = RandomNumberGenerator.Create())
                {
                    random.GetBytes(randomData);
                    for (int i = 0; i < eventsCount; i++)
                    {
                        var evnt = new AFValue(Convert.ToInt32(randomData[i]), start + TimeSpan.FromSeconds(i));
                        eventsToWrite.Add(evnt);
                    }
                }

                // Send the events to be written
                Output.WriteLine($"Write events to PI Point [{pointName}].");
                point.UpdateValues(eventsToWrite, AFUpdateOption.InsertNoCompression);

                // Read the events to verify that the events write was successful
                var eventsRead = new AFValues();

                Output.WriteLine($"Read events from PI Point [{pointName}].");
                AssertEventually.True(() =>
                {
                    eventsRead = point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false);

                    if (eventsToWrite.Count != eventsRead.Count)
                    {
                        Output.WriteLine($"The read event count {eventsRead.Count} does not match the written event count {eventsToWrite.Count}.");
                        return false;
                    }
                    else
                    {
                        for (int j = 0; j < eventsCount; j++)
                        {
                            if (!eventsToWrite[j].Value.Equals(eventsRead[j].Value))
                            {
                                Output.WriteLine($"Written event value {AFFixture.DisplayAFValue(eventsToWrite[j])} " +
                                    $"did not match read event value {AFFixture.DisplayAFValue(eventsRead[j])}.");
                                return false;
                            }
                        }
                    }

                    return true;
                },
                $"The events read back do not match the events written to PI Data Archive [{Fixture.PIServer.Name}].");

                // Delete the events
                Output.WriteLine($"Delete events from PI Point [{pointName}].");
                eventsRead.Clear();
                point.UpdateValues(eventsToWrite, AFUpdateOption.Remove);

                AssertEventually.True(() =>
                {
                    eventsRead = point.RecordedValuesByCount(start, eventsCount, true, AFBoundaryType.Inside, null, false);
                    return eventsRead.Count == 0;
                },
                $"The return event count should be 0 after deleting the events, but it was actually {eventsRead.Count}.");
            }
            finally
            {
                // Delete the PI Point to cleanup
                Fixture.DeletePIPoints(pointName, Output);
            }
        }